inetcomm: Include ws2tcpip.h before anything else.
[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 #include <limits.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "wine/unicode.h"
28
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
34
35 #include "winreg.h"
36 #include "shlwapi.h"
37
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43
44 /* looks-right constants */
45 #define ANCHOR_WIDTH (2.0)
46 #define MAX_ITERS (50)
47
48 /* Converts angle (in degrees) to x/y coordinates */
49 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
50 {
51     REAL radAngle, hypotenuse;
52
53     radAngle = deg2rad(angle);
54     hypotenuse = 50.0; /* arbitrary */
55
56     *x = x_0 + cos(radAngle) * hypotenuse;
57     *y = y_0 + sin(radAngle) * hypotenuse;
58 }
59
60 /* Converts from gdiplus path point type to gdi path point type. */
61 static BYTE convert_path_point_type(BYTE type)
62 {
63     BYTE ret;
64
65     switch(type & PathPointTypePathTypeMask){
66         case PathPointTypeBezier:
67             ret = PT_BEZIERTO;
68             break;
69         case PathPointTypeLine:
70             ret = PT_LINETO;
71             break;
72         case PathPointTypeStart:
73             ret = PT_MOVETO;
74             break;
75         default:
76             ERR("Bad point type\n");
77             return 0;
78     }
79
80     if(type & PathPointTypeCloseSubpath)
81         ret |= PT_CLOSEFIGURE;
82
83     return ret;
84 }
85
86 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
87 {
88     HPEN gdipen;
89     REAL width;
90     INT save_state = SaveDC(graphics->hdc), i, numdashes;
91     GpPointF pt[2];
92     DWORD dash_array[MAX_DASHLEN];
93
94     EndPath(graphics->hdc);
95
96     if(pen->unit == UnitPixel){
97         width = pen->width;
98     }
99     else{
100         /* Get an estimate for the amount the pen width is affected by the world
101          * transform. (This is similar to what some of the wine drivers do.) */
102         pt[0].X = 0.0;
103         pt[0].Y = 0.0;
104         pt[1].X = 1.0;
105         pt[1].Y = 1.0;
106         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
107         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
108                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
109
110         width *= pen->width * convert_unit(graphics->hdc,
111                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
112     }
113
114     if(pen->dash == DashStyleCustom){
115         numdashes = min(pen->numdashes, MAX_DASHLEN);
116
117         TRACE("dashes are: ");
118         for(i = 0; i < numdashes; i++){
119             dash_array[i] = roundr(width * pen->dashes[i]);
120             TRACE("%d, ", dash_array[i]);
121         }
122         TRACE("\n and the pen style is %x\n", pen->style);
123
124         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
125                               numdashes, dash_array);
126     }
127     else
128         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
129
130     SelectObject(graphics->hdc, gdipen);
131
132     return save_state;
133 }
134
135 static void restore_dc(GpGraphics *graphics, INT state)
136 {
137     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
138     RestoreDC(graphics->hdc, state);
139 }
140
141 /* This helper applies all the changes that the points listed in ptf need in
142  * order to be drawn on the device context.  In the end, this should include at
143  * least:
144  *  -scaling by page unit
145  *  -applying world transformation
146  *  -converting from float to int
147  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
148  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
149  * gdi to draw, and these functions would irreparably mess with line widths.
150  */
151 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
152     GpPointF *ptf, INT count)
153 {
154     REAL unitscale;
155     GpMatrix *matrix;
156     int i;
157
158     unitscale = convert_unit(graphics->hdc, graphics->unit);
159
160     /* apply page scale */
161     if(graphics->unit != UnitDisplay)
162         unitscale *= graphics->scale;
163
164     GdipCloneMatrix(graphics->worldtrans, &matrix);
165     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
166     GdipTransformMatrixPoints(matrix, ptf, count);
167     GdipDeleteMatrix(matrix);
168
169     for(i = 0; i < count; i++){
170         pti[i].x = roundr(ptf[i].X);
171         pti[i].y = roundr(ptf[i].Y);
172     }
173 }
174
175 static ARGB blend_colors(ARGB start, ARGB end, int current, int total)
176 {
177     ARGB result=0;
178     ARGB i;
179     for (i=0xff; i<=0xff0000; i = i << 8)
180         result |= (((start&i)*(total - current)+(end&i)*(current))/total)&i;
181     return result;
182 }
183
184 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
185 {
186     switch (brush->bt)
187     {
188     case BrushTypeLinearGradient:
189     {
190         GpLineGradient *line = (GpLineGradient*)brush;
191         RECT rc;
192         int num_steps = 255;
193
194         SelectClipPath(graphics->hdc, RGN_AND);
195         if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
196         {
197             GpPointF endpointsf[2];
198             POINT endpointsi[2];
199             POINT poly[4];
200
201             SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
202
203             /* fill with starting color */
204             FillRect(graphics->hdc, &rc, brush->gdibrush);
205
206             endpointsf[0] = line->startpoint;
207             endpointsf[1] = line->endpoint;
208             transform_and_round_points(graphics, endpointsi, endpointsf, 2);
209
210             if (abs(endpointsi[0].x-endpointsi[1].x) > abs(endpointsi[0].y-endpointsi[1].y))
211             {
212                 /* vertical-ish gradient */
213                 int endborderx; /* vertical rectangle boundary near endpoint */
214                 int startx, endx; /* x co-ordinates of endpoints shifted to intersect the top of the visible rectangle */
215                 int startbottomx, endbottomx; /* x co-ordinate of endpoints shifted to intersect the bottom of the visible rectangle */
216                 int width;
217                 COLORREF col;
218                 HBRUSH hbrush, hprevbrush;
219                 int i;
220
221                 if (endpointsi[1].x > endpointsi[0].x)
222                     endborderx = rc.right;
223                 else
224                     endborderx = rc.left;
225
226                 startx = roundr((rc.top - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
227                 endx = roundr((rc.top - endpointsf[1].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[1].X);
228                 width = endx - startx;
229                 startbottomx = roundr((rc.bottom - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
230                 endbottomx = startbottomx+width;
231
232                 if (num_steps > abs(width)) num_steps = abs(width);
233
234                 poly[0].x = endborderx;
235                 poly[0].y = rc.bottom;
236                 poly[1].x = endborderx;
237                 poly[1].y = rc.top;
238                 poly[2].y = rc.top;
239                 poly[3].y = rc.bottom;
240
241                 for (i=1; i<num_steps; i++)
242                 {
243                     ARGB argb = blend_colors(line->startcolor, line->endcolor, i, num_steps);
244                     int ofs = width * i / num_steps;
245                     col = ARGB2COLORREF(argb);
246                     hbrush = CreateSolidBrush(col);
247                     hprevbrush = SelectObject(graphics->hdc, hbrush);
248                     poly[2].x = startx + ofs;
249                     poly[3].x = startbottomx + ofs;
250                     Polygon(graphics->hdc, poly, 4);
251                     SelectObject(graphics->hdc, hprevbrush);
252                     DeleteObject(hbrush);
253                 }
254
255                 poly[2].x = endx;
256                 poly[3].x = endbottomx;
257
258                 /* draw the ending color */
259                 col = ARGB2COLORREF(line->endcolor);
260                 hbrush = CreateSolidBrush(col);
261                 hprevbrush = SelectObject(graphics->hdc, hbrush);
262                 Polygon(graphics->hdc, poly, 4);
263                 SelectObject(graphics->hdc, hprevbrush);
264                 DeleteObject(hbrush);
265             }
266             else if (endpointsi[0].y != endpointsi[1].y)
267             {
268                 /* horizontal-ish gradient */
269                 int endbordery; /* horizontal rectangle boundary near endpoint */
270                 int starty, endy; /* y co-ordinates of endpoints shifted to intersect the left of the visible rectangle */
271                 int startrighty, endrighty; /* y co-ordinate of endpoints shifted to intersect the right of the visible rectangle */
272                 int height;
273                 COLORREF col;
274                 HBRUSH hbrush, hprevbrush;
275                 int i;
276
277                 if (endpointsi[1].y > endpointsi[0].y)
278                     endbordery = rc.bottom;
279                 else
280                     endbordery = rc.top;
281
282                 starty = roundr((rc.left - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
283                 endy = roundr((rc.left - endpointsf[1].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[1].Y);
284                 height = endy - starty;
285                 startrighty = roundr((rc.right - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
286                 endrighty = startrighty+height;
287
288                 if (num_steps > abs(height)) num_steps = abs(height);
289
290                 poly[0].x = rc.right;
291                 poly[0].y = endbordery;
292                 poly[1].x = rc.left;
293                 poly[1].y = endbordery;
294                 poly[2].x = rc.left;
295                 poly[3].x = rc.right;
296
297                 for (i=1; i<num_steps; i++)
298                 {
299                     ARGB argb = blend_colors(line->startcolor, line->endcolor, i, num_steps);
300                     int ofs = height * i / num_steps;
301                     col = ARGB2COLORREF(argb);
302                     hbrush = CreateSolidBrush(col);
303                     hprevbrush = SelectObject(graphics->hdc, hbrush);
304                     poly[2].y = starty + ofs;
305                     poly[3].y = startrighty + ofs;
306                     Polygon(graphics->hdc, poly, 4);
307                     SelectObject(graphics->hdc, hprevbrush);
308                     DeleteObject(hbrush);
309                 }
310
311                 poly[2].y = endy;
312                 poly[3].y = endrighty;
313
314                 /* draw the ending color */
315                 col = ARGB2COLORREF(line->endcolor);
316                 hbrush = CreateSolidBrush(col);
317                 hprevbrush = SelectObject(graphics->hdc, hbrush);
318                 Polygon(graphics->hdc, poly, 4);
319                 SelectObject(graphics->hdc, hprevbrush);
320                 DeleteObject(hbrush);
321             }
322             /* else startpoint == endpoint */
323         }
324         break;
325     }
326     default:
327         SelectObject(graphics->hdc, brush->gdibrush);
328         FillPath(graphics->hdc);
329         break;
330     }
331 }
332
333 /* GdipDrawPie/GdipFillPie helper function */
334 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
335     REAL height, REAL startAngle, REAL sweepAngle)
336 {
337     GpPointF ptf[4];
338     POINT pti[4];
339
340     ptf[0].X = x;
341     ptf[0].Y = y;
342     ptf[1].X = x + width;
343     ptf[1].Y = y + height;
344
345     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
346     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
347
348     transform_and_round_points(graphics, pti, ptf, 4);
349
350     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
351         pti[2].y, pti[3].x, pti[3].y);
352 }
353
354 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
355  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
356  * should not be called on an hdc that has a path you care about. */
357 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
358     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
359 {
360     HGDIOBJ oldbrush = NULL, oldpen = NULL;
361     GpMatrix *matrix = NULL;
362     HBRUSH brush = NULL;
363     HPEN pen = NULL;
364     PointF ptf[4], *custptf = NULL;
365     POINT pt[4], *custpt = NULL;
366     BYTE *tp = NULL;
367     REAL theta, dsmall, dbig, dx, dy = 0.0;
368     INT i, count;
369     LOGBRUSH lb;
370     BOOL customstroke;
371
372     if((x1 == x2) && (y1 == y2))
373         return;
374
375     theta = gdiplus_atan2(y2 - y1, x2 - x1);
376
377     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
378     if(!customstroke){
379         brush = CreateSolidBrush(color);
380         lb.lbStyle = BS_SOLID;
381         lb.lbColor = color;
382         lb.lbHatch = 0;
383         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
384                            PS_JOIN_MITER, 1, &lb, 0,
385                            NULL);
386         oldbrush = SelectObject(graphics->hdc, brush);
387         oldpen = SelectObject(graphics->hdc, pen);
388     }
389
390     switch(cap){
391         case LineCapFlat:
392             break;
393         case LineCapSquare:
394         case LineCapSquareAnchor:
395         case LineCapDiamondAnchor:
396             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
397             if(cap == LineCapDiamondAnchor){
398                 dsmall = cos(theta + M_PI_2) * size;
399                 dbig = sin(theta + M_PI_2) * size;
400             }
401             else{
402                 dsmall = cos(theta + M_PI_4) * size;
403                 dbig = sin(theta + M_PI_4) * size;
404             }
405
406             ptf[0].X = x2 - dsmall;
407             ptf[1].X = x2 + dbig;
408
409             ptf[0].Y = y2 - dbig;
410             ptf[3].Y = y2 + dsmall;
411
412             ptf[1].Y = y2 - dsmall;
413             ptf[2].Y = y2 + dbig;
414
415             ptf[3].X = x2 - dbig;
416             ptf[2].X = x2 + dsmall;
417
418             transform_and_round_points(graphics, pt, ptf, 4);
419             Polygon(graphics->hdc, pt, 4);
420
421             break;
422         case LineCapArrowAnchor:
423             size = size * 4.0 / sqrt(3.0);
424
425             dx = cos(M_PI / 6.0 + theta) * size;
426             dy = sin(M_PI / 6.0 + theta) * size;
427
428             ptf[0].X = x2 - dx;
429             ptf[0].Y = y2 - dy;
430
431             dx = cos(- M_PI / 6.0 + theta) * size;
432             dy = sin(- M_PI / 6.0 + theta) * size;
433
434             ptf[1].X = x2 - dx;
435             ptf[1].Y = y2 - dy;
436
437             ptf[2].X = x2;
438             ptf[2].Y = y2;
439
440             transform_and_round_points(graphics, pt, ptf, 3);
441             Polygon(graphics->hdc, pt, 3);
442
443             break;
444         case LineCapRoundAnchor:
445             dx = dy = ANCHOR_WIDTH * size / 2.0;
446
447             ptf[0].X = x2 - dx;
448             ptf[0].Y = y2 - dy;
449             ptf[1].X = x2 + dx;
450             ptf[1].Y = y2 + dy;
451
452             transform_and_round_points(graphics, pt, ptf, 2);
453             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
454
455             break;
456         case LineCapTriangle:
457             size = size / 2.0;
458             dx = cos(M_PI_2 + theta) * size;
459             dy = sin(M_PI_2 + theta) * size;
460
461             ptf[0].X = x2 - dx;
462             ptf[0].Y = y2 - dy;
463             ptf[1].X = x2 + dx;
464             ptf[1].Y = y2 + dy;
465
466             dx = cos(theta) * size;
467             dy = sin(theta) * size;
468
469             ptf[2].X = x2 + dx;
470             ptf[2].Y = y2 + dy;
471
472             transform_and_round_points(graphics, pt, ptf, 3);
473             Polygon(graphics->hdc, pt, 3);
474
475             break;
476         case LineCapRound:
477             dx = dy = size / 2.0;
478
479             ptf[0].X = x2 - dx;
480             ptf[0].Y = y2 - dy;
481             ptf[1].X = x2 + dx;
482             ptf[1].Y = y2 + dy;
483
484             dx = -cos(M_PI_2 + theta) * size;
485             dy = -sin(M_PI_2 + theta) * size;
486
487             ptf[2].X = x2 - dx;
488             ptf[2].Y = y2 - dy;
489             ptf[3].X = x2 + dx;
490             ptf[3].Y = y2 + dy;
491
492             transform_and_round_points(graphics, pt, ptf, 4);
493             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
494                 pt[2].y, pt[3].x, pt[3].y);
495
496             break;
497         case LineCapCustom:
498             if(!custom)
499                 break;
500
501             count = custom->pathdata.Count;
502             custptf = GdipAlloc(count * sizeof(PointF));
503             custpt = GdipAlloc(count * sizeof(POINT));
504             tp = GdipAlloc(count);
505
506             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
507                 goto custend;
508
509             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
510
511             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
512             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
513                              MatrixOrderAppend);
514             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
515             GdipTransformMatrixPoints(matrix, custptf, count);
516
517             transform_and_round_points(graphics, custpt, custptf, count);
518
519             for(i = 0; i < count; i++)
520                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
521
522             if(custom->fill){
523                 BeginPath(graphics->hdc);
524                 PolyDraw(graphics->hdc, custpt, tp, count);
525                 EndPath(graphics->hdc);
526                 StrokeAndFillPath(graphics->hdc);
527             }
528             else
529                 PolyDraw(graphics->hdc, custpt, tp, count);
530
531 custend:
532             GdipFree(custptf);
533             GdipFree(custpt);
534             GdipFree(tp);
535             GdipDeleteMatrix(matrix);
536             break;
537         default:
538             break;
539     }
540
541     if(!customstroke){
542         SelectObject(graphics->hdc, oldbrush);
543         SelectObject(graphics->hdc, oldpen);
544         DeleteObject(brush);
545         DeleteObject(pen);
546     }
547 }
548
549 /* Shortens the line by the given percent by changing x2, y2.
550  * If percent is > 1.0 then the line will change direction.
551  * If percent is negative it can lengthen the line. */
552 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
553 {
554     REAL dist, theta, dx, dy;
555
556     if((y1 == *y2) && (x1 == *x2))
557         return;
558
559     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
560     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
561     dx = cos(theta) * dist;
562     dy = sin(theta) * dist;
563
564     *x2 = *x2 + dx;
565     *y2 = *y2 + dy;
566 }
567
568 /* Shortens the line by the given amount by changing x2, y2.
569  * If the amount is greater than the distance, the line will become length 0.
570  * If the amount is negative, it can lengthen the line. */
571 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
572 {
573     REAL dx, dy, percent;
574
575     dx = *x2 - x1;
576     dy = *y2 - y1;
577     if(dx == 0 && dy == 0)
578         return;
579
580     percent = amt / sqrt(dx * dx + dy * dy);
581     if(percent >= 1.0){
582         *x2 = x1;
583         *y2 = y1;
584         return;
585     }
586
587     shorten_line_percent(x1, y1, x2, y2, percent);
588 }
589
590 /* Draws lines between the given points, and if caps is true then draws an endcap
591  * at the end of the last line. */
592 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
593     GDIPCONST GpPointF * pt, INT count, BOOL caps)
594 {
595     POINT *pti = NULL;
596     GpPointF *ptcopy = NULL;
597     GpStatus status = GenericError;
598
599     if(!count)
600         return Ok;
601
602     pti = GdipAlloc(count * sizeof(POINT));
603     ptcopy = GdipAlloc(count * sizeof(GpPointF));
604
605     if(!pti || !ptcopy){
606         status = OutOfMemory;
607         goto end;
608     }
609
610     memcpy(ptcopy, pt, count * sizeof(GpPointF));
611
612     if(caps){
613         if(pen->endcap == LineCapArrowAnchor)
614             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
615                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
616         else if((pen->endcap == LineCapCustom) && pen->customend)
617             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
618                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
619                              pen->customend->inset * pen->width);
620
621         if(pen->startcap == LineCapArrowAnchor)
622             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
623                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
624         else if((pen->startcap == LineCapCustom) && pen->customstart)
625             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
626                              &ptcopy[0].X, &ptcopy[0].Y,
627                              pen->customstart->inset * pen->width);
628
629         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
630                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
631         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
632                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
633     }
634
635     transform_and_round_points(graphics, pti, ptcopy, count);
636
637     if(Polyline(graphics->hdc, pti, count))
638         status = Ok;
639
640 end:
641     GdipFree(pti);
642     GdipFree(ptcopy);
643
644     return status;
645 }
646
647 /* Conducts a linear search to find the bezier points that will back off
648  * the endpoint of the curve by a distance of amt. Linear search works
649  * better than binary in this case because there are multiple solutions,
650  * and binary searches often find a bad one. I don't think this is what
651  * Windows does but short of rendering the bezier without GDI's help it's
652  * the best we can do. If rev then work from the start of the passed points
653  * instead of the end. */
654 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
655 {
656     GpPointF origpt[4];
657     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
658     INT i, first = 0, second = 1, third = 2, fourth = 3;
659
660     if(rev){
661         first = 3;
662         second = 2;
663         third = 1;
664         fourth = 0;
665     }
666
667     origx = pt[fourth].X;
668     origy = pt[fourth].Y;
669     memcpy(origpt, pt, sizeof(GpPointF) * 4);
670
671     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
672         /* reset bezier points to original values */
673         memcpy(pt, origpt, sizeof(GpPointF) * 4);
674         /* Perform magic on bezier points. Order is important here.*/
675         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
676         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
677         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
678         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
679         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
680         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
681
682         dx = pt[fourth].X - origx;
683         dy = pt[fourth].Y - origy;
684
685         diff = sqrt(dx * dx + dy * dy);
686         percent += 0.0005 * amt;
687     }
688 }
689
690 /* Draws bezier curves between given points, and if caps is true then draws an
691  * endcap at the end of the last line. */
692 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
693     GDIPCONST GpPointF * pt, INT count, BOOL caps)
694 {
695     POINT *pti;
696     GpPointF *ptcopy;
697     GpStatus status = GenericError;
698
699     if(!count)
700         return Ok;
701
702     pti = GdipAlloc(count * sizeof(POINT));
703     ptcopy = GdipAlloc(count * sizeof(GpPointF));
704
705     if(!pti || !ptcopy){
706         status = OutOfMemory;
707         goto end;
708     }
709
710     memcpy(ptcopy, pt, count * sizeof(GpPointF));
711
712     if(caps){
713         if(pen->endcap == LineCapArrowAnchor)
714             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
715         else if((pen->endcap == LineCapCustom) && pen->customend)
716             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
717                                FALSE);
718
719         if(pen->startcap == LineCapArrowAnchor)
720             shorten_bezier_amt(ptcopy, pen->width, TRUE);
721         else if((pen->startcap == LineCapCustom) && pen->customstart)
722             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
723
724         /* the direction of the line cap is parallel to the direction at the
725          * end of the bezier (which, if it has been shortened, is not the same
726          * as the direction from pt[count-2] to pt[count-1]) */
727         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
728             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
729             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
730             pt[count - 1].X, pt[count - 1].Y);
731
732         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
733             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
734             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
735     }
736
737     transform_and_round_points(graphics, pti, ptcopy, count);
738
739     PolyBezier(graphics->hdc, pti, count);
740
741     status = Ok;
742
743 end:
744     GdipFree(pti);
745     GdipFree(ptcopy);
746
747     return status;
748 }
749
750 /* Draws a combination of bezier curves and lines between points. */
751 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
752     GDIPCONST BYTE * types, INT count, BOOL caps)
753 {
754     POINT *pti = GdipAlloc(count * sizeof(POINT));
755     BYTE *tp = GdipAlloc(count);
756     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
757     INT i, j;
758     GpStatus status = GenericError;
759
760     if(!count){
761         status = Ok;
762         goto end;
763     }
764     if(!pti || !tp || !ptcopy){
765         status = OutOfMemory;
766         goto end;
767     }
768
769     for(i = 1; i < count; i++){
770         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
771             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
772                 || !(types[i + 1] & PathPointTypeBezier)){
773                 ERR("Bad bezier points\n");
774                 goto end;
775             }
776             i += 2;
777         }
778     }
779
780     memcpy(ptcopy, pt, count * sizeof(GpPointF));
781
782     /* If we are drawing caps, go through the points and adjust them accordingly,
783      * and draw the caps. */
784     if(caps){
785         switch(types[count - 1] & PathPointTypePathTypeMask){
786             case PathPointTypeBezier:
787                 if(pen->endcap == LineCapArrowAnchor)
788                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
789                 else if((pen->endcap == LineCapCustom) && pen->customend)
790                     shorten_bezier_amt(&ptcopy[count - 4],
791                                        pen->width * pen->customend->inset, FALSE);
792
793                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
794                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
795                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
796                     pt[count - 1].X, pt[count - 1].Y);
797
798                 break;
799             case PathPointTypeLine:
800                 if(pen->endcap == LineCapArrowAnchor)
801                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
802                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
803                                      pen->width);
804                 else if((pen->endcap == LineCapCustom) && pen->customend)
805                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
806                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
807                                      pen->customend->inset * pen->width);
808
809                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
810                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
811                          pt[count - 1].Y);
812
813                 break;
814             default:
815                 ERR("Bad path last point\n");
816                 goto end;
817         }
818
819         /* Find start of points */
820         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
821             == PathPointTypeStart); j++);
822
823         switch(types[j] & PathPointTypePathTypeMask){
824             case PathPointTypeBezier:
825                 if(pen->startcap == LineCapArrowAnchor)
826                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
827                 else if((pen->startcap == LineCapCustom) && pen->customstart)
828                     shorten_bezier_amt(&ptcopy[j - 1],
829                                        pen->width * pen->customstart->inset, TRUE);
830
831                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
832                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
833                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
834                     pt[j - 1].X, pt[j - 1].Y);
835
836                 break;
837             case PathPointTypeLine:
838                 if(pen->startcap == LineCapArrowAnchor)
839                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
840                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
841                                      pen->width);
842                 else if((pen->startcap == LineCapCustom) && pen->customstart)
843                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
844                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
845                                      pen->customstart->inset * pen->width);
846
847                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
848                          pt[j].X, pt[j].Y, pt[j - 1].X,
849                          pt[j - 1].Y);
850
851                 break;
852             default:
853                 ERR("Bad path points\n");
854                 goto end;
855         }
856     }
857
858     transform_and_round_points(graphics, pti, ptcopy, count);
859
860     for(i = 0; i < count; i++){
861         tp[i] = convert_path_point_type(types[i]);
862     }
863
864     PolyDraw(graphics->hdc, pti, tp, count);
865
866     status = Ok;
867
868 end:
869     GdipFree(pti);
870     GdipFree(ptcopy);
871     GdipFree(tp);
872
873     return status;
874 }
875
876 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
877 {
878     GpStatus result;
879
880     BeginPath(graphics->hdc);
881     result = draw_poly(graphics, NULL, path->pathdata.Points,
882                        path->pathdata.Types, path->pathdata.Count, FALSE);
883     EndPath(graphics->hdc);
884     return result;
885 }
886
887 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
888 {
889     TRACE("(%p, %p)\n", hdc, graphics);
890
891     return GdipCreateFromHDC2(hdc, NULL, graphics);
892 }
893
894 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
895 {
896     GpStatus retval;
897
898     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
899
900     if(hDevice != NULL) {
901         FIXME("Don't know how to hadle parameter hDevice\n");
902         return NotImplemented;
903     }
904
905     if(hdc == NULL)
906         return OutOfMemory;
907
908     if(graphics == NULL)
909         return InvalidParameter;
910
911     *graphics = GdipAlloc(sizeof(GpGraphics));
912     if(!*graphics)  return OutOfMemory;
913
914     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
915         GdipFree(*graphics);
916         return retval;
917     }
918
919     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
920         GdipFree((*graphics)->worldtrans);
921         GdipFree(*graphics);
922         return retval;
923     }
924
925     (*graphics)->hdc = hdc;
926     (*graphics)->hwnd = WindowFromDC(hdc);
927     (*graphics)->smoothing = SmoothingModeDefault;
928     (*graphics)->compqual = CompositingQualityDefault;
929     (*graphics)->interpolation = InterpolationModeDefault;
930     (*graphics)->pixeloffset = PixelOffsetModeDefault;
931     (*graphics)->compmode = CompositingModeSourceOver;
932     (*graphics)->unit = UnitDisplay;
933     (*graphics)->scale = 1.0;
934     (*graphics)->busy = FALSE;
935     (*graphics)->textcontrast = 4;
936
937     return Ok;
938 }
939
940 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
941 {
942     GpStatus ret;
943
944     TRACE("(%p, %p)\n", hwnd, graphics);
945
946     if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok)
947         return ret;
948
949     (*graphics)->hwnd = hwnd;
950
951     return Ok;
952 }
953
954 /* FIXME: no icm handling */
955 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
956 {
957     TRACE("(%p, %p)\n", hwnd, graphics);
958
959     return GdipCreateFromHWND(hwnd, graphics);
960 }
961
962 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
963     GpMetafile **metafile)
964 {
965     static int calls;
966
967     if(!hemf || !metafile)
968         return InvalidParameter;
969
970     if(!(calls++))
971         FIXME("not implemented\n");
972
973     return NotImplemented;
974 }
975
976 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
977     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
978 {
979     IStream *stream = NULL;
980     UINT read;
981     BYTE* copy;
982     HENHMETAFILE hemf;
983     GpStatus retval = GenericError;
984
985     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
986
987     if(!hwmf || !metafile || !placeable)
988         return InvalidParameter;
989
990     *metafile = NULL;
991     read = GetMetaFileBitsEx(hwmf, 0, NULL);
992     if(!read)
993         return GenericError;
994     copy = GdipAlloc(read);
995     GetMetaFileBitsEx(hwmf, read, copy);
996
997     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
998     GdipFree(copy);
999
1000     read = GetEnhMetaFileBits(hemf, 0, NULL);
1001     copy = GdipAlloc(read);
1002     GetEnhMetaFileBits(hemf, read, copy);
1003     DeleteEnhMetaFile(hemf);
1004
1005     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
1006         ERR("could not make stream\n");
1007         GdipFree(copy);
1008         goto err;
1009     }
1010
1011     *metafile = GdipAlloc(sizeof(GpMetafile));
1012     if(!*metafile){
1013         retval = OutOfMemory;
1014         goto err;
1015     }
1016
1017     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
1018         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
1019         goto err;
1020
1021
1022     (*metafile)->image.type = ImageTypeMetafile;
1023     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
1024     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Right) / ((REAL) placeable->Inch);
1025     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
1026                     - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
1027     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
1028                    - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
1029     (*metafile)->unit = UnitInch;
1030
1031     if(delete)
1032         DeleteMetaFile(hwmf);
1033
1034     return Ok;
1035
1036 err:
1037     GdipFree(*metafile);
1038     IStream_Release(stream);
1039     return retval;
1040 }
1041
1042 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1043     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1044 {
1045     HMETAFILE hmf = GetMetaFileW(file);
1046
1047     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1048
1049     if(!hmf) return InvalidParameter;
1050
1051     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1052 }
1053
1054 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
1055     UINT access, IStream **stream)
1056 {
1057     DWORD dwMode;
1058     HRESULT ret;
1059
1060     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
1061
1062     if(!stream || !filename)
1063         return InvalidParameter;
1064
1065     if(access & GENERIC_WRITE)
1066         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
1067     else if(access & GENERIC_READ)
1068         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
1069     else
1070         return InvalidParameter;
1071
1072     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
1073
1074     return hresult_to_status(ret);
1075 }
1076
1077 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
1078 {
1079     TRACE("(%p)\n", graphics);
1080
1081     if(!graphics) return InvalidParameter;
1082     if(graphics->busy) return ObjectBusy;
1083
1084     if(graphics->hwnd)
1085         ReleaseDC(graphics->hwnd, graphics->hdc);
1086
1087     GdipDeleteRegion(graphics->clip);
1088     GdipDeleteMatrix(graphics->worldtrans);
1089     GdipFree(graphics);
1090
1091     return Ok;
1092 }
1093
1094 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
1095     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1096 {
1097     INT save_state, num_pts;
1098     GpPointF points[MAX_ARC_PTS];
1099     GpStatus retval;
1100
1101     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1102           width, height, startAngle, sweepAngle);
1103
1104     if(!graphics || !pen || width <= 0 || height <= 0)
1105         return InvalidParameter;
1106
1107     if(graphics->busy)
1108         return ObjectBusy;
1109
1110     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
1111
1112     save_state = prepare_dc(graphics, pen);
1113
1114     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
1115
1116     restore_dc(graphics, save_state);
1117
1118     return retval;
1119 }
1120
1121 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
1122     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1123 {
1124     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1125           width, height, startAngle, sweepAngle);
1126
1127     return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1128 }
1129
1130 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
1131     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
1132 {
1133     INT save_state;
1134     GpPointF pt[4];
1135     GpStatus retval;
1136
1137     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
1138           x2, y2, x3, y3, x4, y4);
1139
1140     if(!graphics || !pen)
1141         return InvalidParameter;
1142
1143     if(graphics->busy)
1144         return ObjectBusy;
1145
1146     pt[0].X = x1;
1147     pt[0].Y = y1;
1148     pt[1].X = x2;
1149     pt[1].Y = y2;
1150     pt[2].X = x3;
1151     pt[2].Y = y3;
1152     pt[3].X = x4;
1153     pt[3].Y = y4;
1154
1155     save_state = prepare_dc(graphics, pen);
1156
1157     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1158
1159     restore_dc(graphics, save_state);
1160
1161     return retval;
1162 }
1163
1164 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
1165     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
1166 {
1167     INT save_state;
1168     GpPointF pt[4];
1169     GpStatus retval;
1170
1171     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1172           x2, y2, x3, y3, x4, y4);
1173
1174     if(!graphics || !pen)
1175         return InvalidParameter;
1176
1177     if(graphics->busy)
1178         return ObjectBusy;
1179
1180     pt[0].X = x1;
1181     pt[0].Y = y1;
1182     pt[1].X = x2;
1183     pt[1].Y = y2;
1184     pt[2].X = x3;
1185     pt[2].Y = y3;
1186     pt[3].X = x4;
1187     pt[3].Y = y4;
1188
1189     save_state = prepare_dc(graphics, pen);
1190
1191     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1192
1193     restore_dc(graphics, save_state);
1194
1195     return retval;
1196 }
1197
1198 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1199     GDIPCONST GpPointF *points, INT count)
1200 {
1201     INT i;
1202     GpStatus ret;
1203
1204     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1205
1206     if(!graphics || !pen || !points || (count <= 0))
1207         return InvalidParameter;
1208
1209     if(graphics->busy)
1210         return ObjectBusy;
1211
1212     for(i = 0; i < floor(count / 4); i++){
1213         ret = GdipDrawBezier(graphics, pen,
1214                              points[4*i].X, points[4*i].Y,
1215                              points[4*i + 1].X, points[4*i + 1].Y,
1216                              points[4*i + 2].X, points[4*i + 2].Y,
1217                              points[4*i + 3].X, points[4*i + 3].Y);
1218         if(ret != Ok)
1219             return ret;
1220     }
1221
1222     return Ok;
1223 }
1224
1225 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1226     GDIPCONST GpPoint *points, INT count)
1227 {
1228     GpPointF *pts;
1229     GpStatus ret;
1230     INT i;
1231
1232     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1233
1234     if(!graphics || !pen || !points || (count <= 0))
1235         return InvalidParameter;
1236
1237     if(graphics->busy)
1238         return ObjectBusy;
1239
1240     pts = GdipAlloc(sizeof(GpPointF) * count);
1241     if(!pts)
1242         return OutOfMemory;
1243
1244     for(i = 0; i < count; i++){
1245         pts[i].X = (REAL)points[i].X;
1246         pts[i].Y = (REAL)points[i].Y;
1247     }
1248
1249     ret = GdipDrawBeziers(graphics,pen,pts,count);
1250
1251     GdipFree(pts);
1252
1253     return ret;
1254 }
1255
1256 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1257     GDIPCONST GpPointF *points, INT count)
1258 {
1259     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1260
1261     return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1262 }
1263
1264 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1265     GDIPCONST GpPoint *points, INT count)
1266 {
1267     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1268
1269     return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1270 }
1271
1272 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1273     GDIPCONST GpPointF *points, INT count, REAL tension)
1274 {
1275     GpPath *path;
1276     GpStatus stat;
1277
1278     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1279
1280     if(!graphics || !pen || !points || count <= 0)
1281         return InvalidParameter;
1282
1283     if(graphics->busy)
1284         return ObjectBusy;
1285
1286     if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1287         return stat;
1288
1289     stat = GdipAddPathClosedCurve2(path, points, count, tension);
1290     if(stat != Ok){
1291         GdipDeletePath(path);
1292         return stat;
1293     }
1294
1295     stat = GdipDrawPath(graphics, pen, path);
1296
1297     GdipDeletePath(path);
1298
1299     return stat;
1300 }
1301
1302 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1303     GDIPCONST GpPoint *points, INT count, REAL tension)
1304 {
1305     GpPointF *ptf;
1306     GpStatus stat;
1307     INT i;
1308
1309     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1310
1311     if(!points || count <= 0)
1312         return InvalidParameter;
1313
1314     ptf = GdipAlloc(sizeof(GpPointF)*count);
1315     if(!ptf)
1316         return OutOfMemory;
1317
1318     for(i = 0; i < count; i++){
1319         ptf[i].X = (REAL)points[i].X;
1320         ptf[i].Y = (REAL)points[i].Y;
1321     }
1322
1323     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1324
1325     GdipFree(ptf);
1326
1327     return stat;
1328 }
1329
1330 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1331     GDIPCONST GpPointF *points, INT count)
1332 {
1333     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1334
1335     return GdipDrawCurve2(graphics,pen,points,count,1.0);
1336 }
1337
1338 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1339     GDIPCONST GpPoint *points, INT count)
1340 {
1341     GpPointF *pointsF;
1342     GpStatus ret;
1343     INT i;
1344
1345     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1346
1347     if(!points || count <= 0)
1348         return InvalidParameter;
1349
1350     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1351     if(!pointsF)
1352         return OutOfMemory;
1353
1354     for(i = 0; i < count; i++){
1355         pointsF[i].X = (REAL)points[i].X;
1356         pointsF[i].Y = (REAL)points[i].Y;
1357     }
1358
1359     ret = GdipDrawCurve(graphics,pen,pointsF,count);
1360     GdipFree(pointsF);
1361
1362     return ret;
1363 }
1364
1365 /* Approximates cardinal spline with Bezier curves. */
1366 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1367     GDIPCONST GpPointF *points, INT count, REAL tension)
1368 {
1369     /* PolyBezier expects count*3-2 points. */
1370     INT i, len_pt = count*3-2, save_state;
1371     GpPointF *pt;
1372     REAL x1, x2, y1, y2;
1373     GpStatus retval;
1374
1375     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1376
1377     if(!graphics || !pen)
1378         return InvalidParameter;
1379
1380     if(graphics->busy)
1381         return ObjectBusy;
1382
1383     pt = GdipAlloc(len_pt * sizeof(GpPointF));
1384     tension = tension * TENSION_CONST;
1385
1386     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1387         tension, &x1, &y1);
1388
1389     pt[0].X = points[0].X;
1390     pt[0].Y = points[0].Y;
1391     pt[1].X = x1;
1392     pt[1].Y = y1;
1393
1394     for(i = 0; i < count-2; i++){
1395         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1396
1397         pt[3*i+2].X = x1;
1398         pt[3*i+2].Y = y1;
1399         pt[3*i+3].X = points[i+1].X;
1400         pt[3*i+3].Y = points[i+1].Y;
1401         pt[3*i+4].X = x2;
1402         pt[3*i+4].Y = y2;
1403     }
1404
1405     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
1406         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
1407
1408     pt[len_pt-2].X = x1;
1409     pt[len_pt-2].Y = y1;
1410     pt[len_pt-1].X = points[count-1].X;
1411     pt[len_pt-1].Y = points[count-1].Y;
1412
1413     save_state = prepare_dc(graphics, pen);
1414
1415     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1416
1417     GdipFree(pt);
1418     restore_dc(graphics, save_state);
1419
1420     return retval;
1421 }
1422
1423 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1424     GDIPCONST GpPoint *points, INT count, REAL tension)
1425 {
1426     GpPointF *pointsF;
1427     GpStatus ret;
1428     INT i;
1429
1430     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1431
1432     if(!points || count <= 0)
1433         return InvalidParameter;
1434
1435     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1436     if(!pointsF)
1437         return OutOfMemory;
1438
1439     for(i = 0; i < count; i++){
1440         pointsF[i].X = (REAL)points[i].X;
1441         pointsF[i].Y = (REAL)points[i].Y;
1442     }
1443
1444     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1445     GdipFree(pointsF);
1446
1447     return ret;
1448 }
1449
1450 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1451     REAL y, REAL width, REAL height)
1452 {
1453     INT save_state;
1454     GpPointF ptf[2];
1455     POINT pti[2];
1456
1457     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1458
1459     if(!graphics || !pen)
1460         return InvalidParameter;
1461
1462     if(graphics->busy)
1463         return ObjectBusy;
1464
1465     ptf[0].X = x;
1466     ptf[0].Y = y;
1467     ptf[1].X = x + width;
1468     ptf[1].Y = y + height;
1469
1470     save_state = prepare_dc(graphics, pen);
1471     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1472
1473     transform_and_round_points(graphics, pti, ptf, 2);
1474
1475     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1476
1477     restore_dc(graphics, save_state);
1478
1479     return Ok;
1480 }
1481
1482 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1483     INT y, INT width, INT height)
1484 {
1485     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1486
1487     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1488 }
1489
1490
1491 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1492 {
1493     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1494
1495     /* IPicture::Render uses LONG coords */
1496     return GdipDrawImageI(graphics,image,roundr(x),roundr(y));
1497 }
1498
1499 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1500     INT y)
1501 {
1502     UINT width, height, srcw, srch;
1503
1504     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1505
1506     if(!graphics || !image)
1507         return InvalidParameter;
1508
1509     GdipGetImageWidth(image, &width);
1510     GdipGetImageHeight(image, &height);
1511
1512     srcw = width * (((REAL) INCH_HIMETRIC) /
1513             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1514     srch = height * (((REAL) INCH_HIMETRIC) /
1515             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1516
1517     if(image->type != ImageTypeMetafile){
1518         y += height;
1519         height *= -1;
1520     }
1521
1522     IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1523                     0, 0, srcw, srch, NULL);
1524
1525     return Ok;
1526 }
1527
1528 /* FIXME: partially implemented (only works for rectangular parallelograms) */
1529 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1530      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1531      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1532      DrawImageAbort callback, VOID * callbackData)
1533 {
1534     GpPointF ptf[3];
1535     POINT pti[3];
1536     REAL dx, dy;
1537
1538     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1539           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1540           callbackData);
1541
1542     if(!graphics || !image || !points || count != 3)
1543          return InvalidParameter;
1544
1545     if(srcUnit == UnitInch)
1546         dx = dy = (REAL) INCH_HIMETRIC;
1547     else if(srcUnit == UnitPixel){
1548         dx = ((REAL) INCH_HIMETRIC) /
1549              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1550         dy = ((REAL) INCH_HIMETRIC) /
1551              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1552     }
1553     else
1554         return NotImplemented;
1555
1556     memcpy(ptf, points, 3 * sizeof(GpPointF));
1557     transform_and_round_points(graphics, pti, ptf, 3);
1558
1559     /* IPicture renders bitmaps with the y-axis reversed
1560      * FIXME: flipping for unknown image type might not be correct. */
1561     if(image->type != ImageTypeMetafile){
1562         INT temp;
1563         temp = pti[0].y;
1564         pti[0].y = pti[2].y;
1565         pti[2].y = temp;
1566     }
1567
1568     if(IPicture_Render(image->picture, graphics->hdc,
1569         pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1570         srcx * dx, srcy * dy,
1571         srcwidth * dx, srcheight * dy,
1572         NULL) != S_OK){
1573         if(callback)
1574             callback(callbackData);
1575         return GenericError;
1576     }
1577
1578     return Ok;
1579 }
1580
1581 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
1582      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
1583      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1584      DrawImageAbort callback, VOID * callbackData)
1585 {
1586     GpPointF pointsF[3];
1587     INT i;
1588
1589     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
1590           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1591           callbackData);
1592
1593     if(!points || count!=3)
1594         return InvalidParameter;
1595
1596     for(i = 0; i < count; i++){
1597         pointsF[i].X = (REAL)points[i].X;
1598         pointsF[i].Y = (REAL)points[i].Y;
1599     }
1600
1601     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
1602                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
1603                                    callback, callbackData);
1604 }
1605
1606 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
1607     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
1608     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
1609     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
1610     VOID * callbackData)
1611 {
1612     GpPointF points[3];
1613
1614     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
1615           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1616           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1617
1618     points[0].X = dstx;
1619     points[0].Y = dsty;
1620     points[1].X = dstx + dstwidth;
1621     points[1].Y = dsty;
1622     points[2].X = dstx;
1623     points[2].Y = dsty + dstheight;
1624
1625     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1626                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1627 }
1628
1629 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
1630         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
1631         INT srcwidth, INT srcheight, GpUnit srcUnit,
1632         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
1633         VOID * callbackData)
1634 {
1635     GpPointF points[3];
1636
1637     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
1638           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1639           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1640
1641     points[0].X = dstx;
1642     points[0].Y = dsty;
1643     points[1].X = dstx + dstwidth;
1644     points[1].Y = dsty;
1645     points[2].X = dstx;
1646     points[2].Y = dsty + dstheight;
1647
1648     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1649                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1650 }
1651
1652 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
1653     REAL x, REAL y, REAL width, REAL height)
1654 {
1655     RectF bounds;
1656     GpUnit unit;
1657     GpStatus ret;
1658
1659     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
1660
1661     if(!graphics || !image)
1662         return InvalidParameter;
1663
1664     ret = GdipGetImageBounds(image, &bounds, &unit);
1665     if(ret != Ok)
1666         return ret;
1667
1668     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
1669                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
1670                                  unit, NULL, NULL, NULL);
1671 }
1672
1673 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
1674     INT x, INT y, INT width, INT height)
1675 {
1676     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
1677
1678     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
1679 }
1680
1681 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1682     REAL y1, REAL x2, REAL y2)
1683 {
1684     INT save_state;
1685     GpPointF pt[2];
1686     GpStatus retval;
1687
1688     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
1689
1690     if(!pen || !graphics)
1691         return InvalidParameter;
1692
1693     if(graphics->busy)
1694         return ObjectBusy;
1695
1696     pt[0].X = x1;
1697     pt[0].Y = y1;
1698     pt[1].X = x2;
1699     pt[1].Y = y2;
1700
1701     save_state = prepare_dc(graphics, pen);
1702
1703     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1704
1705     restore_dc(graphics, save_state);
1706
1707     return retval;
1708 }
1709
1710 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1711     INT y1, INT x2, INT y2)
1712 {
1713     INT save_state;
1714     GpPointF pt[2];
1715     GpStatus retval;
1716
1717     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
1718
1719     if(!pen || !graphics)
1720         return InvalidParameter;
1721
1722     if(graphics->busy)
1723         return ObjectBusy;
1724
1725     pt[0].X = (REAL)x1;
1726     pt[0].Y = (REAL)y1;
1727     pt[1].X = (REAL)x2;
1728     pt[1].Y = (REAL)y2;
1729
1730     save_state = prepare_dc(graphics, pen);
1731
1732     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1733
1734     restore_dc(graphics, save_state);
1735
1736     return retval;
1737 }
1738
1739 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1740     GpPointF *points, INT count)
1741 {
1742     INT save_state;
1743     GpStatus retval;
1744
1745     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1746
1747     if(!pen || !graphics || (count < 2))
1748         return InvalidParameter;
1749
1750     if(graphics->busy)
1751         return ObjectBusy;
1752
1753     save_state = prepare_dc(graphics, pen);
1754
1755     retval = draw_polyline(graphics, pen, points, count, TRUE);
1756
1757     restore_dc(graphics, save_state);
1758
1759     return retval;
1760 }
1761
1762 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
1763     GpPoint *points, INT count)
1764 {
1765     INT save_state;
1766     GpStatus retval;
1767     GpPointF *ptf = NULL;
1768     int i;
1769
1770     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1771
1772     if(!pen || !graphics || (count < 2))
1773         return InvalidParameter;
1774
1775     if(graphics->busy)
1776         return ObjectBusy;
1777
1778     ptf = GdipAlloc(count * sizeof(GpPointF));
1779     if(!ptf) return OutOfMemory;
1780
1781     for(i = 0; i < count; i ++){
1782         ptf[i].X = (REAL) points[i].X;
1783         ptf[i].Y = (REAL) points[i].Y;
1784     }
1785
1786     save_state = prepare_dc(graphics, pen);
1787
1788     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
1789
1790     restore_dc(graphics, save_state);
1791
1792     GdipFree(ptf);
1793     return retval;
1794 }
1795
1796 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1797 {
1798     INT save_state;
1799     GpStatus retval;
1800
1801     TRACE("(%p, %p, %p)\n", graphics, pen, path);
1802
1803     if(!pen || !graphics)
1804         return InvalidParameter;
1805
1806     if(graphics->busy)
1807         return ObjectBusy;
1808
1809     save_state = prepare_dc(graphics, pen);
1810
1811     retval = draw_poly(graphics, pen, path->pathdata.Points,
1812                        path->pathdata.Types, path->pathdata.Count, TRUE);
1813
1814     restore_dc(graphics, save_state);
1815
1816     return retval;
1817 }
1818
1819 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1820     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1821 {
1822     INT save_state;
1823
1824     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1825             width, height, startAngle, sweepAngle);
1826
1827     if(!graphics || !pen)
1828         return InvalidParameter;
1829
1830     if(graphics->busy)
1831         return ObjectBusy;
1832
1833     save_state = prepare_dc(graphics, pen);
1834     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1835
1836     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1837
1838     restore_dc(graphics, save_state);
1839
1840     return Ok;
1841 }
1842
1843 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
1844     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1845 {
1846     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1847             width, height, startAngle, sweepAngle);
1848
1849     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1850 }
1851
1852 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
1853     REAL y, REAL width, REAL height)
1854 {
1855     INT save_state;
1856     GpPointF ptf[4];
1857     POINT pti[4];
1858
1859     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1860
1861     if(!pen || !graphics)
1862         return InvalidParameter;
1863
1864     if(graphics->busy)
1865         return ObjectBusy;
1866
1867     ptf[0].X = x;
1868     ptf[0].Y = y;
1869     ptf[1].X = x + width;
1870     ptf[1].Y = y;
1871     ptf[2].X = x + width;
1872     ptf[2].Y = y + height;
1873     ptf[3].X = x;
1874     ptf[3].Y = y + height;
1875
1876     save_state = prepare_dc(graphics, pen);
1877     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1878
1879     transform_and_round_points(graphics, pti, ptf, 4);
1880     Polygon(graphics->hdc, pti, 4);
1881
1882     restore_dc(graphics, save_state);
1883
1884     return Ok;
1885 }
1886
1887 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
1888     INT y, INT width, INT height)
1889 {
1890     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1891
1892     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1893 }
1894
1895 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
1896     GDIPCONST GpRectF* rects, INT count)
1897 {
1898     GpPointF *ptf;
1899     POINT *pti;
1900     INT save_state, i;
1901
1902     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1903
1904     if(!graphics || !pen || !rects || count < 1)
1905         return InvalidParameter;
1906
1907     if(graphics->busy)
1908         return ObjectBusy;
1909
1910     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
1911     pti = GdipAlloc(4 * count * sizeof(POINT));
1912
1913     if(!ptf || !pti){
1914         GdipFree(ptf);
1915         GdipFree(pti);
1916         return OutOfMemory;
1917     }
1918
1919     for(i = 0; i < count; i++){
1920         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
1921         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
1922         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
1923         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
1924     }
1925
1926     save_state = prepare_dc(graphics, pen);
1927     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1928
1929     transform_and_round_points(graphics, pti, ptf, 4 * count);
1930
1931     for(i = 0; i < count; i++)
1932         Polygon(graphics->hdc, &pti[4 * i], 4);
1933
1934     restore_dc(graphics, save_state);
1935
1936     GdipFree(ptf);
1937     GdipFree(pti);
1938
1939     return Ok;
1940 }
1941
1942 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
1943     GDIPCONST GpRect* rects, INT count)
1944 {
1945     GpRectF *rectsF;
1946     GpStatus ret;
1947     INT i;
1948
1949     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1950
1951     if(!rects || count<=0)
1952         return InvalidParameter;
1953
1954     rectsF = GdipAlloc(sizeof(GpRectF) * count);
1955     if(!rectsF)
1956         return OutOfMemory;
1957
1958     for(i = 0;i < count;i++){
1959         rectsF[i].X      = (REAL)rects[i].X;
1960         rectsF[i].Y      = (REAL)rects[i].Y;
1961         rectsF[i].Width  = (REAL)rects[i].Width;
1962         rectsF[i].Height = (REAL)rects[i].Height;
1963     }
1964
1965     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
1966     GdipFree(rectsF);
1967
1968     return ret;
1969 }
1970
1971 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
1972     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
1973     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
1974 {
1975     HRGN rgn = NULL;
1976     HFONT gdifont;
1977     LOGFONTW lfw;
1978     TEXTMETRICW textmet;
1979     GpPointF pt[2], rectcpy[4];
1980     POINT corners[4];
1981     WCHAR* stringdup;
1982     REAL angle, ang_cos, ang_sin, rel_width, rel_height;
1983     INT sum = 0, height = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
1984         nheight;
1985     SIZE size;
1986     RECT drawcoord;
1987
1988     if(!graphics || !string || !font || !brush || !rect)
1989         return InvalidParameter;
1990
1991     if((brush->bt != BrushTypeSolidColor)){
1992         FIXME("not implemented for given parameters\n");
1993         return NotImplemented;
1994     }
1995
1996     if(format)
1997         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
1998
1999     if(length == -1) length = lstrlenW(string);
2000
2001     stringdup = GdipAlloc(length * sizeof(WCHAR));
2002     if(!stringdup) return OutOfMemory;
2003
2004     save_state = SaveDC(graphics->hdc);
2005     SetBkMode(graphics->hdc, TRANSPARENT);
2006     SetTextColor(graphics->hdc, brush->lb.lbColor);
2007
2008     rectcpy[3].X = rectcpy[0].X = rect->X;
2009     rectcpy[1].Y = rectcpy[0].Y = rect->Y;
2010     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
2011     rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
2012     transform_and_round_points(graphics, corners, rectcpy, 4);
2013
2014     if(roundr(rect->Width) == 0 && roundr(rect->Height) == 0){
2015         rel_width = rel_height = 1.0;
2016         nwidth = nheight = INT_MAX;
2017     }
2018     else{
2019         rel_width = sqrt((corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) +
2020                          (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y))
2021                          / rect->Width;
2022         rel_height = sqrt((corners[2].x - corners[1].x) * (corners[2].x - corners[1].x) +
2023                           (corners[2].y - corners[1].y) * (corners[2].y - corners[1].y))
2024                           / rect->Height;
2025
2026         nwidth = roundr(rel_width * rect->Width);
2027         nheight = roundr(rel_height * rect->Height);
2028         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
2029         SelectClipRgn(graphics->hdc, rgn);
2030     }
2031
2032     /* Use gdi to find the font, then perform transformations on it (height,
2033      * width, angle). */
2034     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2035     GetTextMetricsW(graphics->hdc, &textmet);
2036     lfw = font->lfw;
2037
2038     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
2039     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
2040
2041     pt[0].X = 0.0;
2042     pt[0].Y = 0.0;
2043     pt[1].X = 1.0;
2044     pt[1].Y = 0.0;
2045     GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
2046     angle = gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2047     ang_cos = cos(angle);
2048     ang_sin = sin(angle);
2049     lfw.lfEscapement = lfw.lfOrientation = -roundr((angle / M_PI) * 1800.0);
2050
2051     gdifont = CreateFontIndirectW(&lfw);
2052     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
2053
2054     for(i = 0, j = 0; i < length; i++){
2055         if(!isprintW(string[i]) && (string[i] != '\n'))
2056             continue;
2057
2058         stringdup[j] = string[i];
2059         j++;
2060     }
2061
2062     length = j;
2063
2064     while(sum < length){
2065         drawcoord.left = corners[0].x + roundr(ang_sin * (REAL) height);
2066         drawcoord.top = corners[0].y + roundr(ang_cos * (REAL) height);
2067
2068         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2069                               nwidth, &fit, NULL, &size);
2070         fitcpy = fit;
2071
2072         if(fit == 0){
2073             DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, DT_NOCLIP |
2074                       DT_EXPANDTABS);
2075             break;
2076         }
2077
2078         for(lret = 0; lret < fit; lret++)
2079             if(*(stringdup + sum + lret) == '\n')
2080                 break;
2081
2082         /* Line break code (may look strange, but it imitates windows). */
2083         if(lret < fit)
2084             fit = lret;    /* this is not an off-by-one error */
2085         else if(fit < (length - sum)){
2086             if(*(stringdup + sum + fit) == ' ')
2087                 while(*(stringdup + sum + fit) == ' ')
2088                     fit++;
2089             else
2090                 while(*(stringdup + sum + fit - 1) != ' '){
2091                     fit--;
2092
2093                     if(*(stringdup + sum + fit) == '\t')
2094                         break;
2095
2096                     if(fit == 0){
2097                         fit = fitcpy;
2098                         break;
2099                     }
2100                 }
2101         }
2102         DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
2103                   &drawcoord, DT_NOCLIP | DT_EXPANDTABS);
2104
2105         sum += fit + (lret < fitcpy ? 1 : 0);
2106         height += size.cy;
2107
2108         if(height > nheight)
2109             break;
2110
2111         /* Stop if this was a linewrap (but not if it was a linebreak). */
2112         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2113             break;
2114     }
2115
2116     GdipFree(stringdup);
2117     DeleteObject(rgn);
2118     DeleteObject(gdifont);
2119
2120     RestoreDC(graphics->hdc, save_state);
2121
2122     return Ok;
2123 }
2124
2125 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2126     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2127 {
2128     GpPath *path;
2129     GpStatus stat;
2130
2131     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2132             count, tension, fill);
2133
2134     if(!graphics || !brush || !points)
2135         return InvalidParameter;
2136
2137     if(graphics->busy)
2138         return ObjectBusy;
2139
2140     stat = GdipCreatePath(fill, &path);
2141     if(stat != Ok)
2142         return stat;
2143
2144     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2145     if(stat != Ok){
2146         GdipDeletePath(path);
2147         return stat;
2148     }
2149
2150     stat = GdipFillPath(graphics, brush, path);
2151     if(stat != Ok){
2152         GdipDeletePath(path);
2153         return stat;
2154     }
2155
2156     GdipDeletePath(path);
2157
2158     return Ok;
2159 }
2160
2161 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2162     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2163 {
2164     GpPointF *ptf;
2165     GpStatus stat;
2166     INT i;
2167
2168     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2169             count, tension, fill);
2170
2171     if(!points || count <= 0)
2172         return InvalidParameter;
2173
2174     ptf = GdipAlloc(sizeof(GpPointF)*count);
2175     if(!ptf)
2176         return OutOfMemory;
2177
2178     for(i = 0;i < count;i++){
2179         ptf[i].X = (REAL)points[i].X;
2180         ptf[i].Y = (REAL)points[i].Y;
2181     }
2182
2183     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2184
2185     GdipFree(ptf);
2186
2187     return stat;
2188 }
2189
2190 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2191     REAL y, REAL width, REAL height)
2192 {
2193     INT save_state;
2194     GpPointF ptf[2];
2195     POINT pti[2];
2196
2197     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2198
2199     if(!graphics || !brush)
2200         return InvalidParameter;
2201
2202     if(graphics->busy)
2203         return ObjectBusy;
2204
2205     ptf[0].X = x;
2206     ptf[0].Y = y;
2207     ptf[1].X = x + width;
2208     ptf[1].Y = y + height;
2209
2210     save_state = SaveDC(graphics->hdc);
2211     EndPath(graphics->hdc);
2212     SelectObject(graphics->hdc, brush->gdibrush);
2213     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2214
2215     transform_and_round_points(graphics, pti, ptf, 2);
2216
2217     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2218
2219     RestoreDC(graphics->hdc, save_state);
2220
2221     return Ok;
2222 }
2223
2224 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2225     INT y, INT width, INT height)
2226 {
2227     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2228
2229     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2230 }
2231
2232 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2233 {
2234     INT save_state;
2235     GpStatus retval;
2236
2237     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2238
2239     if(!brush || !graphics || !path)
2240         return InvalidParameter;
2241
2242     if(graphics->busy)
2243         return ObjectBusy;
2244
2245     save_state = SaveDC(graphics->hdc);
2246     EndPath(graphics->hdc);
2247     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2248                                                                     : WINDING));
2249
2250     BeginPath(graphics->hdc);
2251     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2252                        path->pathdata.Types, path->pathdata.Count, FALSE);
2253
2254     if(retval != Ok)
2255         goto end;
2256
2257     EndPath(graphics->hdc);
2258     brush_fill_path(graphics, brush);
2259
2260     retval = Ok;
2261
2262 end:
2263     RestoreDC(graphics->hdc, save_state);
2264
2265     return retval;
2266 }
2267
2268 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2269     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2270 {
2271     INT save_state;
2272
2273     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2274             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2275
2276     if(!graphics || !brush)
2277         return InvalidParameter;
2278
2279     if(graphics->busy)
2280         return ObjectBusy;
2281
2282     save_state = SaveDC(graphics->hdc);
2283     EndPath(graphics->hdc);
2284     SelectObject(graphics->hdc, brush->gdibrush);
2285     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2286
2287     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2288
2289     RestoreDC(graphics->hdc, save_state);
2290
2291     return Ok;
2292 }
2293
2294 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2295     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2296 {
2297     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2298             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2299
2300     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2301 }
2302
2303 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2304     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2305 {
2306     INT save_state;
2307     GpPointF *ptf = NULL;
2308     POINT *pti = NULL;
2309     GpStatus retval = Ok;
2310
2311     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2312
2313     if(!graphics || !brush || !points || !count)
2314         return InvalidParameter;
2315
2316     if(graphics->busy)
2317         return ObjectBusy;
2318
2319     ptf = GdipAlloc(count * sizeof(GpPointF));
2320     pti = GdipAlloc(count * sizeof(POINT));
2321     if(!ptf || !pti){
2322         retval = OutOfMemory;
2323         goto end;
2324     }
2325
2326     memcpy(ptf, points, count * sizeof(GpPointF));
2327
2328     save_state = SaveDC(graphics->hdc);
2329     EndPath(graphics->hdc);
2330     SelectObject(graphics->hdc, brush->gdibrush);
2331     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2332     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2333                                                                   : WINDING));
2334
2335     transform_and_round_points(graphics, pti, ptf, count);
2336     Polygon(graphics->hdc, pti, count);
2337
2338     RestoreDC(graphics->hdc, save_state);
2339
2340 end:
2341     GdipFree(ptf);
2342     GdipFree(pti);
2343
2344     return retval;
2345 }
2346
2347 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2348     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2349 {
2350     INT save_state, i;
2351     GpPointF *ptf = NULL;
2352     POINT *pti = NULL;
2353     GpStatus retval = Ok;
2354
2355     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2356
2357     if(!graphics || !brush || !points || !count)
2358         return InvalidParameter;
2359
2360     if(graphics->busy)
2361         return ObjectBusy;
2362
2363     ptf = GdipAlloc(count * sizeof(GpPointF));
2364     pti = GdipAlloc(count * sizeof(POINT));
2365     if(!ptf || !pti){
2366         retval = OutOfMemory;
2367         goto end;
2368     }
2369
2370     for(i = 0; i < count; i ++){
2371         ptf[i].X = (REAL) points[i].X;
2372         ptf[i].Y = (REAL) points[i].Y;
2373     }
2374
2375     save_state = SaveDC(graphics->hdc);
2376     EndPath(graphics->hdc);
2377     SelectObject(graphics->hdc, brush->gdibrush);
2378     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2379     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2380                                                                   : WINDING));
2381
2382     transform_and_round_points(graphics, pti, ptf, count);
2383     Polygon(graphics->hdc, pti, count);
2384
2385     RestoreDC(graphics->hdc, save_state);
2386
2387 end:
2388     GdipFree(ptf);
2389     GdipFree(pti);
2390
2391     return retval;
2392 }
2393
2394 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2395     GDIPCONST GpPointF *points, INT count)
2396 {
2397     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2398
2399     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2400 }
2401
2402 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2403     GDIPCONST GpPoint *points, INT count)
2404 {
2405     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2406
2407     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2408 }
2409
2410 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2411     REAL x, REAL y, REAL width, REAL height)
2412 {
2413     INT save_state;
2414     GpPointF ptf[4];
2415     POINT pti[4];
2416
2417     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2418
2419     if(!graphics || !brush)
2420         return InvalidParameter;
2421
2422     if(graphics->busy)
2423         return ObjectBusy;
2424
2425     ptf[0].X = x;
2426     ptf[0].Y = y;
2427     ptf[1].X = x + width;
2428     ptf[1].Y = y;
2429     ptf[2].X = x + width;
2430     ptf[2].Y = y + height;
2431     ptf[3].X = x;
2432     ptf[3].Y = y + height;
2433
2434     save_state = SaveDC(graphics->hdc);
2435     EndPath(graphics->hdc);
2436     SelectObject(graphics->hdc, brush->gdibrush);
2437     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2438
2439     transform_and_round_points(graphics, pti, ptf, 4);
2440
2441     Polygon(graphics->hdc, pti, 4);
2442
2443     RestoreDC(graphics->hdc, save_state);
2444
2445     return Ok;
2446 }
2447
2448 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2449     INT x, INT y, INT width, INT height)
2450 {
2451     INT save_state;
2452     GpPointF ptf[4];
2453     POINT pti[4];
2454
2455     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2456
2457     if(!graphics || !brush)
2458         return InvalidParameter;
2459
2460     if(graphics->busy)
2461         return ObjectBusy;
2462
2463     ptf[0].X = x;
2464     ptf[0].Y = y;
2465     ptf[1].X = x + width;
2466     ptf[1].Y = y;
2467     ptf[2].X = x + width;
2468     ptf[2].Y = y + height;
2469     ptf[3].X = x;
2470     ptf[3].Y = y + height;
2471
2472     save_state = SaveDC(graphics->hdc);
2473     EndPath(graphics->hdc);
2474     SelectObject(graphics->hdc, brush->gdibrush);
2475     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2476
2477     transform_and_round_points(graphics, pti, ptf, 4);
2478
2479     Polygon(graphics->hdc, pti, 4);
2480
2481     RestoreDC(graphics->hdc, save_state);
2482
2483     return Ok;
2484 }
2485
2486 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2487     INT count)
2488 {
2489     GpStatus ret;
2490     INT i;
2491
2492     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2493
2494     if(!rects)
2495         return InvalidParameter;
2496
2497     for(i = 0; i < count; i++){
2498         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2499         if(ret != Ok)   return ret;
2500     }
2501
2502     return Ok;
2503 }
2504
2505 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2506     INT count)
2507 {
2508     GpRectF *rectsF;
2509     GpStatus ret;
2510     INT i;
2511
2512     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2513
2514     if(!rects || count <= 0)
2515         return InvalidParameter;
2516
2517     rectsF = GdipAlloc(sizeof(GpRectF)*count);
2518     if(!rectsF)
2519         return OutOfMemory;
2520
2521     for(i = 0; i < count; i++){
2522         rectsF[i].X      = (REAL)rects[i].X;
2523         rectsF[i].Y      = (REAL)rects[i].Y;
2524         rectsF[i].X      = (REAL)rects[i].Width;
2525         rectsF[i].Height = (REAL)rects[i].Height;
2526     }
2527
2528     ret = GdipFillRectangles(graphics,brush,rectsF,count);
2529     GdipFree(rectsF);
2530
2531     return ret;
2532 }
2533
2534 /*****************************************************************************
2535  * GdipFillRegion [GDIPLUS.@]
2536  */
2537 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2538         GpRegion* region)
2539 {
2540     INT save_state;
2541     GpStatus status;
2542     HRGN hrgn;
2543
2544     TRACE("(%p, %p, %p)\n", graphics, brush, region);
2545
2546     if (!(graphics && brush && region))
2547         return InvalidParameter;
2548
2549     if(graphics->busy)
2550         return ObjectBusy;
2551
2552     status = GdipGetRegionHRgn(region, graphics, &hrgn);
2553     if(status != Ok)
2554         return status;
2555
2556     save_state = SaveDC(graphics->hdc);
2557     EndPath(graphics->hdc);
2558     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2559
2560     FillRgn(graphics->hdc, hrgn, brush->gdibrush);
2561
2562     RestoreDC(graphics->hdc, save_state);
2563
2564     DeleteObject(hrgn);
2565
2566     return Ok;
2567 }
2568
2569 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2570 {
2571     static int calls;
2572
2573     if(!graphics)
2574         return InvalidParameter;
2575
2576     if(graphics->busy)
2577         return ObjectBusy;
2578
2579     if(!(calls++))
2580         FIXME("not implemented\n");
2581
2582     return NotImplemented;
2583 }
2584
2585 /*****************************************************************************
2586  * GdipGetClipBounds [GDIPLUS.@]
2587  */
2588 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
2589 {
2590     TRACE("(%p, %p)\n", graphics, rect);
2591
2592     if(!graphics)
2593         return InvalidParameter;
2594
2595     if(graphics->busy)
2596         return ObjectBusy;
2597
2598     return GdipGetRegionBounds(graphics->clip, graphics, rect);
2599 }
2600
2601 /*****************************************************************************
2602  * GdipGetClipBoundsI [GDIPLUS.@]
2603  */
2604 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
2605 {
2606     TRACE("(%p, %p)\n", graphics, rect);
2607
2608     if(!graphics)
2609         return InvalidParameter;
2610
2611     if(graphics->busy)
2612         return ObjectBusy;
2613
2614     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
2615 }
2616
2617 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2618 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2619     CompositingMode *mode)
2620 {
2621     TRACE("(%p, %p)\n", graphics, mode);
2622
2623     if(!graphics || !mode)
2624         return InvalidParameter;
2625
2626     if(graphics->busy)
2627         return ObjectBusy;
2628
2629     *mode = graphics->compmode;
2630
2631     return Ok;
2632 }
2633
2634 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2635 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2636     CompositingQuality *quality)
2637 {
2638     TRACE("(%p, %p)\n", graphics, quality);
2639
2640     if(!graphics || !quality)
2641         return InvalidParameter;
2642
2643     if(graphics->busy)
2644         return ObjectBusy;
2645
2646     *quality = graphics->compqual;
2647
2648     return Ok;
2649 }
2650
2651 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2652 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2653     InterpolationMode *mode)
2654 {
2655     TRACE("(%p, %p)\n", graphics, mode);
2656
2657     if(!graphics || !mode)
2658         return InvalidParameter;
2659
2660     if(graphics->busy)
2661         return ObjectBusy;
2662
2663     *mode = graphics->interpolation;
2664
2665     return Ok;
2666 }
2667
2668 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2669 {
2670     if(!graphics || !argb)
2671         return InvalidParameter;
2672
2673     if(graphics->busy)
2674         return ObjectBusy;
2675
2676     FIXME("(%p, %p): stub\n", graphics, argb);
2677
2678     return NotImplemented;
2679 }
2680
2681 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2682 {
2683     TRACE("(%p, %p)\n", graphics, scale);
2684
2685     if(!graphics || !scale)
2686         return InvalidParameter;
2687
2688     if(graphics->busy)
2689         return ObjectBusy;
2690
2691     *scale = graphics->scale;
2692
2693     return Ok;
2694 }
2695
2696 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2697 {
2698     TRACE("(%p, %p)\n", graphics, unit);
2699
2700     if(!graphics || !unit)
2701         return InvalidParameter;
2702
2703     if(graphics->busy)
2704         return ObjectBusy;
2705
2706     *unit = graphics->unit;
2707
2708     return Ok;
2709 }
2710
2711 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2712 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2713     *mode)
2714 {
2715     TRACE("(%p, %p)\n", graphics, mode);
2716
2717     if(!graphics || !mode)
2718         return InvalidParameter;
2719
2720     if(graphics->busy)
2721         return ObjectBusy;
2722
2723     *mode = graphics->pixeloffset;
2724
2725     return Ok;
2726 }
2727
2728 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2729 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2730 {
2731     TRACE("(%p, %p)\n", graphics, mode);
2732
2733     if(!graphics || !mode)
2734         return InvalidParameter;
2735
2736     if(graphics->busy)
2737         return ObjectBusy;
2738
2739     *mode = graphics->smoothing;
2740
2741     return Ok;
2742 }
2743
2744 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2745 {
2746     TRACE("(%p, %p)\n", graphics, contrast);
2747
2748     if(!graphics || !contrast)
2749         return InvalidParameter;
2750
2751     *contrast = graphics->textcontrast;
2752
2753     return Ok;
2754 }
2755
2756 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2757 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2758     TextRenderingHint *hint)
2759 {
2760     TRACE("(%p, %p)\n", graphics, hint);
2761
2762     if(!graphics || !hint)
2763         return InvalidParameter;
2764
2765     if(graphics->busy)
2766         return ObjectBusy;
2767
2768     *hint = graphics->texthint;
2769
2770     return Ok;
2771 }
2772
2773 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2774 {
2775     TRACE("(%p, %p)\n", graphics, matrix);
2776
2777     if(!graphics || !matrix)
2778         return InvalidParameter;
2779
2780     if(graphics->busy)
2781         return ObjectBusy;
2782
2783     *matrix = *graphics->worldtrans;
2784     return Ok;
2785 }
2786
2787 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2788 {
2789     GpSolidFill *brush;
2790     GpStatus stat;
2791     RECT rect;
2792
2793     TRACE("(%p, %x)\n", graphics, color);
2794
2795     if(!graphics)
2796         return InvalidParameter;
2797
2798     if(graphics->busy)
2799         return ObjectBusy;
2800
2801     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2802         return stat;
2803
2804     if(graphics->hwnd){
2805         if(!GetWindowRect(graphics->hwnd, &rect)){
2806             GdipDeleteBrush((GpBrush*)brush);
2807             return GenericError;
2808         }
2809
2810         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right  - rect.left),
2811                                                                (REAL)(rect.bottom - rect.top));
2812     }
2813     else
2814         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2815                                                                (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2816
2817     GdipDeleteBrush((GpBrush*)brush);
2818
2819     return Ok;
2820 }
2821
2822 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2823 {
2824     TRACE("(%p, %p)\n", graphics, res);
2825
2826     if(!graphics || !res)
2827         return InvalidParameter;
2828
2829     return GdipIsEmptyRegion(graphics->clip, graphics, res);
2830 }
2831
2832 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2833 {
2834     FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2835
2836     if(!graphics || !result)
2837         return InvalidParameter;
2838
2839     if(graphics->busy)
2840         return ObjectBusy;
2841
2842     return NotImplemented;
2843 }
2844
2845 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2846 {
2847     FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2848
2849     if(!graphics || !result)
2850         return InvalidParameter;
2851
2852     if(graphics->busy)
2853         return ObjectBusy;
2854
2855     return NotImplemented;
2856 }
2857
2858 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2859         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2860         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2861         INT regionCount, GpRegion** regions)
2862 {
2863     if (!(graphics && string && font && layoutRect && stringFormat && regions))
2864         return InvalidParameter;
2865
2866     FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2867             length, font, layoutRect, stringFormat, regionCount, regions);
2868
2869     return NotImplemented;
2870 }
2871
2872 /* Find the smallest rectangle that bounds the text when it is printed in rect
2873  * according to the format options listed in format. If rect has 0 width and
2874  * height, then just find the smallest rectangle that bounds the text when it's
2875  * printed at location (rect->X, rect-Y). */
2876 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2877     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2878     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2879     INT *codepointsfitted, INT *linesfilled)
2880 {
2881     HFONT oldfont;
2882     WCHAR* stringdup;
2883     INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2884         nheight;
2885     SIZE size;
2886
2887     if(!graphics || !string || !font || !rect)
2888         return InvalidParameter;
2889
2890     if(linesfilled) *linesfilled = 0;
2891     if(codepointsfitted) *codepointsfitted = 0;
2892
2893     if(format)
2894         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2895
2896     if(length == -1) length = lstrlenW(string);
2897
2898     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2899     if(!stringdup) return OutOfMemory;
2900
2901     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2902     nwidth = roundr(rect->Width);
2903     nheight = roundr(rect->Height);
2904
2905     if((nwidth == 0) && (nheight == 0))
2906         nwidth = nheight = INT_MAX;
2907
2908     for(i = 0, j = 0; i < length; i++){
2909         if(!isprintW(string[i]) && (string[i] != '\n'))
2910             continue;
2911
2912         stringdup[j] = string[i];
2913         j++;
2914     }
2915
2916     stringdup[j] = 0;
2917     length = j;
2918
2919     while(sum < length){
2920         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2921                               nwidth, &fit, NULL, &size);
2922         fitcpy = fit;
2923
2924         if(fit == 0)
2925             break;
2926
2927         for(lret = 0; lret < fit; lret++)
2928             if(*(stringdup + sum + lret) == '\n')
2929                 break;
2930
2931         /* Line break code (may look strange, but it imitates windows). */
2932         if(lret < fit)
2933             fit = lret;    /* this is not an off-by-one error */
2934         else if(fit < (length - sum)){
2935             if(*(stringdup + sum + fit) == ' ')
2936                 while(*(stringdup + sum + fit) == ' ')
2937                     fit++;
2938             else
2939                 while(*(stringdup + sum + fit - 1) != ' '){
2940                     fit--;
2941
2942                     if(*(stringdup + sum + fit) == '\t')
2943                         break;
2944
2945                     if(fit == 0){
2946                         fit = fitcpy;
2947                         break;
2948                     }
2949                 }
2950         }
2951
2952         GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2953                               nwidth, &j, NULL, &size);
2954
2955         sum += fit + (lret < fitcpy ? 1 : 0);
2956         if(codepointsfitted) *codepointsfitted = sum;
2957
2958         height += size.cy;
2959         if(linesfilled) *linesfilled += size.cy;
2960         max_width = max(max_width, size.cx);
2961
2962         if(height > nheight)
2963             break;
2964
2965         /* Stop if this was a linewrap (but not if it was a linebreak). */
2966         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2967             break;
2968     }
2969
2970     bounds->X = rect->X;
2971     bounds->Y = rect->Y;
2972     bounds->Width = (REAL)max_width;
2973     bounds->Height = (REAL) min(height, nheight);
2974
2975     GdipFree(stringdup);
2976     DeleteObject(SelectObject(graphics->hdc, oldfont));
2977
2978     return Ok;
2979 }
2980
2981 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
2982 {
2983     TRACE("(%p)\n", graphics);
2984
2985     if(!graphics)
2986         return InvalidParameter;
2987
2988     if(graphics->busy)
2989         return ObjectBusy;
2990
2991     return GdipSetInfinite(graphics->clip);
2992 }
2993
2994 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
2995 {
2996     TRACE("(%p)\n", graphics);
2997
2998     if(!graphics)
2999         return InvalidParameter;
3000
3001     if(graphics->busy)
3002         return ObjectBusy;
3003
3004     graphics->worldtrans->matrix[0] = 1.0;
3005     graphics->worldtrans->matrix[1] = 0.0;
3006     graphics->worldtrans->matrix[2] = 0.0;
3007     graphics->worldtrans->matrix[3] = 1.0;
3008     graphics->worldtrans->matrix[4] = 0.0;
3009     graphics->worldtrans->matrix[5] = 0.0;
3010
3011     return Ok;
3012 }
3013
3014 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3015 {
3016     static int calls;
3017
3018     if(!graphics)
3019         return InvalidParameter;
3020
3021     if(!(calls++))
3022         FIXME("graphics state not implemented\n");
3023
3024     return Ok;
3025 }
3026
3027 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3028     GpMatrixOrder order)
3029 {
3030     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3031
3032     if(!graphics)
3033         return InvalidParameter;
3034
3035     if(graphics->busy)
3036         return ObjectBusy;
3037
3038     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3039 }
3040
3041 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3042 {
3043     static int calls;
3044
3045     if(!graphics || !state)
3046         return InvalidParameter;
3047
3048     if(!(calls++))
3049         FIXME("graphics state not implemented\n");
3050
3051     *state = 0xdeadbeef;
3052     return Ok;
3053 }
3054
3055 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
3056 {
3057     FIXME("(%p, %p)\n", graphics, state);
3058
3059     if(!graphics || !state)
3060         return InvalidParameter;
3061
3062     *state = 0xdeadbeef;
3063     return Ok;
3064 }
3065
3066 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
3067 {
3068     FIXME("(%p, 0x%x)\n", graphics, state);
3069
3070     if(!graphics || !state)
3071         return InvalidParameter;
3072
3073     return Ok;
3074 }
3075
3076 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
3077     REAL sy, GpMatrixOrder order)
3078 {
3079     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
3080
3081     if(!graphics)
3082         return InvalidParameter;
3083
3084     if(graphics->busy)
3085         return ObjectBusy;
3086
3087     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
3088 }
3089
3090 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
3091     CombineMode mode)
3092 {
3093     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
3094
3095     if(!graphics || !srcgraphics)
3096         return InvalidParameter;
3097
3098     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
3099 }
3100
3101 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
3102     CompositingMode mode)
3103 {
3104     TRACE("(%p, %d)\n", graphics, mode);
3105
3106     if(!graphics)
3107         return InvalidParameter;
3108
3109     if(graphics->busy)
3110         return ObjectBusy;
3111
3112     graphics->compmode = mode;
3113
3114     return Ok;
3115 }
3116
3117 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
3118     CompositingQuality quality)
3119 {
3120     TRACE("(%p, %d)\n", graphics, quality);
3121
3122     if(!graphics)
3123         return InvalidParameter;
3124
3125     if(graphics->busy)
3126         return ObjectBusy;
3127
3128     graphics->compqual = quality;
3129
3130     return Ok;
3131 }
3132
3133 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
3134     InterpolationMode mode)
3135 {
3136     TRACE("(%p, %d)\n", graphics, mode);
3137
3138     if(!graphics)
3139         return InvalidParameter;
3140
3141     if(graphics->busy)
3142         return ObjectBusy;
3143
3144     graphics->interpolation = mode;
3145
3146     return Ok;
3147 }
3148
3149 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
3150 {
3151     TRACE("(%p, %.2f)\n", graphics, scale);
3152
3153     if(!graphics || (scale <= 0.0))
3154         return InvalidParameter;
3155
3156     if(graphics->busy)
3157         return ObjectBusy;
3158
3159     graphics->scale = scale;
3160
3161     return Ok;
3162 }
3163
3164 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
3165 {
3166     TRACE("(%p, %d)\n", graphics, unit);
3167
3168     if(!graphics)
3169         return InvalidParameter;
3170
3171     if(graphics->busy)
3172         return ObjectBusy;
3173
3174     if(unit == UnitWorld)
3175         return InvalidParameter;
3176
3177     graphics->unit = unit;
3178
3179     return Ok;
3180 }
3181
3182 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3183     mode)
3184 {
3185     TRACE("(%p, %d)\n", graphics, mode);
3186
3187     if(!graphics)
3188         return InvalidParameter;
3189
3190     if(graphics->busy)
3191         return ObjectBusy;
3192
3193     graphics->pixeloffset = mode;
3194
3195     return Ok;
3196 }
3197
3198 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
3199 {
3200     TRACE("(%p, %d)\n", graphics, mode);
3201
3202     if(!graphics)
3203         return InvalidParameter;
3204
3205     if(graphics->busy)
3206         return ObjectBusy;
3207
3208     graphics->smoothing = mode;
3209
3210     return Ok;
3211 }
3212
3213 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3214 {
3215     TRACE("(%p, %d)\n", graphics, contrast);
3216
3217     if(!graphics)
3218         return InvalidParameter;
3219
3220     graphics->textcontrast = contrast;
3221
3222     return Ok;
3223 }
3224
3225 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3226     TextRenderingHint hint)
3227 {
3228     TRACE("(%p, %d)\n", graphics, hint);
3229
3230     if(!graphics)
3231         return InvalidParameter;
3232
3233     if(graphics->busy)
3234         return ObjectBusy;
3235
3236     graphics->texthint = hint;
3237
3238     return Ok;
3239 }
3240
3241 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3242 {
3243     TRACE("(%p, %p)\n", graphics, matrix);
3244
3245     if(!graphics || !matrix)
3246         return InvalidParameter;
3247
3248     if(graphics->busy)
3249         return ObjectBusy;
3250
3251     GdipDeleteMatrix(graphics->worldtrans);
3252     return GdipCloneMatrix(matrix, &graphics->worldtrans);
3253 }
3254
3255 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3256     REAL dy, GpMatrixOrder order)
3257 {
3258     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3259
3260     if(!graphics)
3261         return InvalidParameter;
3262
3263     if(graphics->busy)
3264         return ObjectBusy;
3265
3266     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3267 }
3268
3269 /*****************************************************************************
3270  * GdipSetClipHrgn [GDIPLUS.@]
3271  */
3272 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
3273 {
3274     GpRegion *region;
3275     GpStatus status;
3276
3277     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
3278
3279     if(!graphics)
3280         return InvalidParameter;
3281
3282     status = GdipCreateRegionHrgn(hrgn, &region);
3283     if(status != Ok)
3284         return status;
3285
3286     status = GdipSetClipRegion(graphics, region, mode);
3287
3288     GdipDeleteRegion(region);
3289     return status;
3290 }
3291
3292 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3293 {
3294     TRACE("(%p, %p, %d)\n", graphics, path, mode);
3295
3296     if(!graphics)
3297         return InvalidParameter;
3298
3299     if(graphics->busy)
3300         return ObjectBusy;
3301
3302     return GdipCombineRegionPath(graphics->clip, path, mode);
3303 }
3304
3305 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3306                                     REAL width, REAL height,
3307                                     CombineMode mode)
3308 {
3309     GpRectF rect;
3310
3311     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3312
3313     if(!graphics)
3314         return InvalidParameter;
3315
3316     if(graphics->busy)
3317         return ObjectBusy;
3318
3319     rect.X = x;
3320     rect.Y = y;
3321     rect.Width  = width;
3322     rect.Height = height;
3323
3324     return GdipCombineRegionRect(graphics->clip, &rect, mode);
3325 }
3326
3327 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3328                                      INT width, INT height,
3329                                      CombineMode mode)
3330 {
3331     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3332
3333     if(!graphics)
3334         return InvalidParameter;
3335
3336     if(graphics->busy)
3337         return ObjectBusy;
3338
3339     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3340 }
3341
3342 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3343                                       CombineMode mode)
3344 {
3345     TRACE("(%p, %p, %d)\n", graphics, region, mode);
3346
3347     if(!graphics || !region)
3348         return InvalidParameter;
3349
3350     if(graphics->busy)
3351         return ObjectBusy;
3352
3353     return GdipCombineRegionRegion(graphics->clip, region, mode);
3354 }
3355
3356 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3357     UINT limitDpi)
3358 {
3359     static int calls;
3360
3361     if(!(calls++))
3362         FIXME("not implemented\n");
3363
3364     return NotImplemented;
3365 }
3366
3367 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3368     INT count)
3369 {
3370     INT save_state;
3371     POINT *pti;
3372
3373     TRACE("(%p, %p, %d)\n", graphics, points, count);
3374
3375     if(!graphics || !pen || count<=0)
3376         return InvalidParameter;
3377
3378     if(graphics->busy)
3379         return ObjectBusy;
3380
3381     pti = GdipAlloc(sizeof(POINT) * count);
3382
3383     save_state = prepare_dc(graphics, pen);
3384     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3385
3386     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3387     Polygon(graphics->hdc, pti, count);
3388
3389     restore_dc(graphics, save_state);
3390     GdipFree(pti);
3391
3392     return Ok;
3393 }
3394
3395 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3396     INT count)
3397 {
3398     GpStatus ret;
3399     GpPointF *ptf;
3400     INT i;
3401
3402     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3403
3404     if(count<=0)    return InvalidParameter;
3405     ptf = GdipAlloc(sizeof(GpPointF) * count);
3406
3407     for(i = 0;i < count; i++){
3408         ptf[i].X = (REAL)points[i].X;
3409         ptf[i].Y = (REAL)points[i].Y;
3410     }
3411
3412     ret = GdipDrawPolygon(graphics,pen,ptf,count);
3413     GdipFree(ptf);
3414
3415     return ret;
3416 }
3417
3418 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3419 {
3420     TRACE("(%p, %p)\n", graphics, dpi);
3421
3422     if(!graphics || !dpi)
3423         return InvalidParameter;
3424
3425     if(graphics->busy)
3426         return ObjectBusy;
3427
3428     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3429
3430     return Ok;
3431 }
3432
3433 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3434 {
3435     TRACE("(%p, %p)\n", graphics, dpi);
3436
3437     if(!graphics || !dpi)
3438         return InvalidParameter;
3439
3440     if(graphics->busy)
3441         return ObjectBusy;
3442
3443     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3444
3445     return Ok;
3446 }
3447
3448 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3449     GpMatrixOrder order)
3450 {
3451     GpMatrix m;
3452     GpStatus ret;
3453
3454     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3455
3456     if(!graphics || !matrix)
3457         return InvalidParameter;
3458
3459     if(graphics->busy)
3460         return ObjectBusy;
3461
3462     m = *(graphics->worldtrans);
3463
3464     ret = GdipMultiplyMatrix(&m, matrix, order);
3465     if(ret == Ok)
3466         *(graphics->worldtrans) = m;
3467
3468     return ret;
3469 }
3470
3471 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3472 {
3473     TRACE("(%p, %p)\n", graphics, hdc);
3474
3475     if(!graphics || !hdc)
3476         return InvalidParameter;
3477
3478     if(graphics->busy)
3479         return ObjectBusy;
3480
3481     *hdc = graphics->hdc;
3482     graphics->busy = TRUE;
3483
3484     return Ok;
3485 }
3486
3487 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3488 {
3489     TRACE("(%p, %p)\n", graphics, hdc);
3490
3491     if(!graphics)
3492         return InvalidParameter;
3493
3494     if(graphics->hdc != hdc || !(graphics->busy))
3495         return InvalidParameter;
3496
3497     graphics->busy = FALSE;
3498
3499     return Ok;
3500 }
3501
3502 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3503 {
3504     GpRegion *clip;
3505     GpStatus status;
3506
3507     TRACE("(%p, %p)\n", graphics, region);
3508
3509     if(!graphics || !region)
3510         return InvalidParameter;
3511
3512     if(graphics->busy)
3513         return ObjectBusy;
3514
3515     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3516         return status;
3517
3518     /* free everything except root node and header */
3519     delete_element(&region->node);
3520     memcpy(region, clip, sizeof(GpRegion));
3521
3522     return Ok;
3523 }
3524
3525 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3526                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
3527 {
3528     if(!graphics || !points || count <= 0)
3529         return InvalidParameter;
3530
3531     if(graphics->busy)
3532         return ObjectBusy;
3533
3534     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3535
3536     return NotImplemented;
3537 }
3538
3539 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3540                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
3541 {
3542     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3543
3544     return NotImplemented;
3545 }
3546
3547 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3548 {
3549     FIXME("\n");
3550
3551     return NULL;
3552 }
3553
3554 /*****************************************************************************
3555  * GdipTranslateClip [GDIPLUS.@]
3556  */
3557 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
3558 {
3559     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
3560
3561     if(!graphics)
3562         return InvalidParameter;
3563
3564     if(graphics->busy)
3565         return ObjectBusy;
3566
3567     return GdipTranslateRegion(graphics->clip, dx, dy);
3568 }
3569
3570 /*****************************************************************************
3571  * GdipTranslateClipI [GDIPLUS.@]
3572  */
3573 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
3574 {
3575     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
3576
3577     if(!graphics)
3578         return InvalidParameter;
3579
3580     if(graphics->busy)
3581         return ObjectBusy;
3582
3583     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
3584 }