mciqtz32: Remove all remaining tabs.
[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)
2015     {
2016         rel_width = 1.0;
2017         nwidth = INT_MAX;
2018     }
2019     else
2020     {
2021         rel_width = sqrt((corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) +
2022                          (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y))
2023                          / rect->Width;
2024         nwidth = roundr(rel_width * rect->Width);
2025     }
2026
2027     if (roundr(rect->Height) == 0)
2028     {
2029         rel_height = 1.0;
2030         nheight = INT_MAX;
2031     }
2032     else
2033     {
2034         rel_height = sqrt((corners[2].x - corners[1].x) * (corners[2].x - corners[1].x) +
2035                           (corners[2].y - corners[1].y) * (corners[2].y - corners[1].y))
2036                           / rect->Height;
2037         nheight = roundr(rel_height * rect->Height);
2038     }
2039
2040     if (roundr(rect->Width) != 0 && roundr(rect->Height) != 0)
2041     {
2042         /* FIXME: If only the width or only the height is 0, we should probably still clip */
2043         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
2044         SelectClipRgn(graphics->hdc, rgn);
2045     }
2046
2047     /* Use gdi to find the font, then perform transformations on it (height,
2048      * width, angle). */
2049     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2050     GetTextMetricsW(graphics->hdc, &textmet);
2051     lfw = font->lfw;
2052
2053     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
2054     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
2055
2056     pt[0].X = 0.0;
2057     pt[0].Y = 0.0;
2058     pt[1].X = 1.0;
2059     pt[1].Y = 0.0;
2060     GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
2061     angle = gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2062     ang_cos = cos(angle);
2063     ang_sin = sin(angle);
2064     lfw.lfEscapement = lfw.lfOrientation = -roundr((angle / M_PI) * 1800.0);
2065
2066     gdifont = CreateFontIndirectW(&lfw);
2067     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
2068
2069     for(i = 0, j = 0; i < length; i++){
2070         if(!isprintW(string[i]) && (string[i] != '\n'))
2071             continue;
2072
2073         stringdup[j] = string[i];
2074         j++;
2075     }
2076
2077     length = j;
2078
2079     while(sum < length){
2080         drawcoord.left = corners[0].x + roundr(ang_sin * (REAL) height);
2081         drawcoord.top = corners[0].y + roundr(ang_cos * (REAL) height);
2082
2083         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2084                               nwidth, &fit, NULL, &size);
2085         fitcpy = fit;
2086
2087         if(fit == 0){
2088             DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, DT_NOCLIP |
2089                       DT_EXPANDTABS);
2090             break;
2091         }
2092
2093         for(lret = 0; lret < fit; lret++)
2094             if(*(stringdup + sum + lret) == '\n')
2095                 break;
2096
2097         /* Line break code (may look strange, but it imitates windows). */
2098         if(lret < fit)
2099             fit = lret;    /* this is not an off-by-one error */
2100         else if(fit < (length - sum)){
2101             if(*(stringdup + sum + fit) == ' ')
2102                 while(*(stringdup + sum + fit) == ' ')
2103                     fit++;
2104             else
2105                 while(*(stringdup + sum + fit - 1) != ' '){
2106                     fit--;
2107
2108                     if(*(stringdup + sum + fit) == '\t')
2109                         break;
2110
2111                     if(fit == 0){
2112                         fit = fitcpy;
2113                         break;
2114                     }
2115                 }
2116         }
2117         DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
2118                   &drawcoord, DT_NOCLIP | DT_EXPANDTABS);
2119
2120         sum += fit + (lret < fitcpy ? 1 : 0);
2121         height += size.cy;
2122
2123         if(height > nheight)
2124             break;
2125
2126         /* Stop if this was a linewrap (but not if it was a linebreak). */
2127         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2128             break;
2129     }
2130
2131     GdipFree(stringdup);
2132     DeleteObject(rgn);
2133     DeleteObject(gdifont);
2134
2135     RestoreDC(graphics->hdc, save_state);
2136
2137     return Ok;
2138 }
2139
2140 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2141     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2142 {
2143     GpPath *path;
2144     GpStatus stat;
2145
2146     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2147             count, tension, fill);
2148
2149     if(!graphics || !brush || !points)
2150         return InvalidParameter;
2151
2152     if(graphics->busy)
2153         return ObjectBusy;
2154
2155     stat = GdipCreatePath(fill, &path);
2156     if(stat != Ok)
2157         return stat;
2158
2159     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2160     if(stat != Ok){
2161         GdipDeletePath(path);
2162         return stat;
2163     }
2164
2165     stat = GdipFillPath(graphics, brush, path);
2166     if(stat != Ok){
2167         GdipDeletePath(path);
2168         return stat;
2169     }
2170
2171     GdipDeletePath(path);
2172
2173     return Ok;
2174 }
2175
2176 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2177     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2178 {
2179     GpPointF *ptf;
2180     GpStatus stat;
2181     INT i;
2182
2183     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2184             count, tension, fill);
2185
2186     if(!points || count <= 0)
2187         return InvalidParameter;
2188
2189     ptf = GdipAlloc(sizeof(GpPointF)*count);
2190     if(!ptf)
2191         return OutOfMemory;
2192
2193     for(i = 0;i < count;i++){
2194         ptf[i].X = (REAL)points[i].X;
2195         ptf[i].Y = (REAL)points[i].Y;
2196     }
2197
2198     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2199
2200     GdipFree(ptf);
2201
2202     return stat;
2203 }
2204
2205 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2206     REAL y, REAL width, REAL height)
2207 {
2208     INT save_state;
2209     GpPointF ptf[2];
2210     POINT pti[2];
2211
2212     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2213
2214     if(!graphics || !brush)
2215         return InvalidParameter;
2216
2217     if(graphics->busy)
2218         return ObjectBusy;
2219
2220     ptf[0].X = x;
2221     ptf[0].Y = y;
2222     ptf[1].X = x + width;
2223     ptf[1].Y = y + height;
2224
2225     save_state = SaveDC(graphics->hdc);
2226     EndPath(graphics->hdc);
2227     SelectObject(graphics->hdc, brush->gdibrush);
2228     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2229
2230     transform_and_round_points(graphics, pti, ptf, 2);
2231
2232     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2233
2234     RestoreDC(graphics->hdc, save_state);
2235
2236     return Ok;
2237 }
2238
2239 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2240     INT y, INT width, INT height)
2241 {
2242     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2243
2244     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2245 }
2246
2247 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2248 {
2249     INT save_state;
2250     GpStatus retval;
2251
2252     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2253
2254     if(!brush || !graphics || !path)
2255         return InvalidParameter;
2256
2257     if(graphics->busy)
2258         return ObjectBusy;
2259
2260     save_state = SaveDC(graphics->hdc);
2261     EndPath(graphics->hdc);
2262     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2263                                                                     : WINDING));
2264
2265     BeginPath(graphics->hdc);
2266     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2267                        path->pathdata.Types, path->pathdata.Count, FALSE);
2268
2269     if(retval != Ok)
2270         goto end;
2271
2272     EndPath(graphics->hdc);
2273     brush_fill_path(graphics, brush);
2274
2275     retval = Ok;
2276
2277 end:
2278     RestoreDC(graphics->hdc, save_state);
2279
2280     return retval;
2281 }
2282
2283 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2284     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2285 {
2286     INT save_state;
2287
2288     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2289             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2290
2291     if(!graphics || !brush)
2292         return InvalidParameter;
2293
2294     if(graphics->busy)
2295         return ObjectBusy;
2296
2297     save_state = SaveDC(graphics->hdc);
2298     EndPath(graphics->hdc);
2299     SelectObject(graphics->hdc, brush->gdibrush);
2300     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2301
2302     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2303
2304     RestoreDC(graphics->hdc, save_state);
2305
2306     return Ok;
2307 }
2308
2309 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2310     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2311 {
2312     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2313             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2314
2315     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2316 }
2317
2318 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2319     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2320 {
2321     INT save_state;
2322     GpPointF *ptf = NULL;
2323     POINT *pti = NULL;
2324     GpStatus retval = Ok;
2325
2326     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2327
2328     if(!graphics || !brush || !points || !count)
2329         return InvalidParameter;
2330
2331     if(graphics->busy)
2332         return ObjectBusy;
2333
2334     ptf = GdipAlloc(count * sizeof(GpPointF));
2335     pti = GdipAlloc(count * sizeof(POINT));
2336     if(!ptf || !pti){
2337         retval = OutOfMemory;
2338         goto end;
2339     }
2340
2341     memcpy(ptf, points, count * sizeof(GpPointF));
2342
2343     save_state = SaveDC(graphics->hdc);
2344     EndPath(graphics->hdc);
2345     SelectObject(graphics->hdc, brush->gdibrush);
2346     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2347     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2348                                                                   : WINDING));
2349
2350     transform_and_round_points(graphics, pti, ptf, count);
2351     Polygon(graphics->hdc, pti, count);
2352
2353     RestoreDC(graphics->hdc, save_state);
2354
2355 end:
2356     GdipFree(ptf);
2357     GdipFree(pti);
2358
2359     return retval;
2360 }
2361
2362 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2363     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2364 {
2365     INT save_state, i;
2366     GpPointF *ptf = NULL;
2367     POINT *pti = NULL;
2368     GpStatus retval = Ok;
2369
2370     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2371
2372     if(!graphics || !brush || !points || !count)
2373         return InvalidParameter;
2374
2375     if(graphics->busy)
2376         return ObjectBusy;
2377
2378     ptf = GdipAlloc(count * sizeof(GpPointF));
2379     pti = GdipAlloc(count * sizeof(POINT));
2380     if(!ptf || !pti){
2381         retval = OutOfMemory;
2382         goto end;
2383     }
2384
2385     for(i = 0; i < count; i ++){
2386         ptf[i].X = (REAL) points[i].X;
2387         ptf[i].Y = (REAL) points[i].Y;
2388     }
2389
2390     save_state = SaveDC(graphics->hdc);
2391     EndPath(graphics->hdc);
2392     SelectObject(graphics->hdc, brush->gdibrush);
2393     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2394     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2395                                                                   : WINDING));
2396
2397     transform_and_round_points(graphics, pti, ptf, count);
2398     Polygon(graphics->hdc, pti, count);
2399
2400     RestoreDC(graphics->hdc, save_state);
2401
2402 end:
2403     GdipFree(ptf);
2404     GdipFree(pti);
2405
2406     return retval;
2407 }
2408
2409 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2410     GDIPCONST GpPointF *points, INT count)
2411 {
2412     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2413
2414     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2415 }
2416
2417 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2418     GDIPCONST GpPoint *points, INT count)
2419 {
2420     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2421
2422     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2423 }
2424
2425 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2426     REAL x, REAL y, REAL width, REAL height)
2427 {
2428     INT save_state;
2429     GpPointF ptf[4];
2430     POINT pti[4];
2431
2432     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2433
2434     if(!graphics || !brush)
2435         return InvalidParameter;
2436
2437     if(graphics->busy)
2438         return ObjectBusy;
2439
2440     ptf[0].X = x;
2441     ptf[0].Y = y;
2442     ptf[1].X = x + width;
2443     ptf[1].Y = y;
2444     ptf[2].X = x + width;
2445     ptf[2].Y = y + height;
2446     ptf[3].X = x;
2447     ptf[3].Y = y + height;
2448
2449     save_state = SaveDC(graphics->hdc);
2450     EndPath(graphics->hdc);
2451     SelectObject(graphics->hdc, brush->gdibrush);
2452     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2453
2454     transform_and_round_points(graphics, pti, ptf, 4);
2455
2456     Polygon(graphics->hdc, pti, 4);
2457
2458     RestoreDC(graphics->hdc, save_state);
2459
2460     return Ok;
2461 }
2462
2463 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2464     INT x, INT y, INT width, INT height)
2465 {
2466     INT save_state;
2467     GpPointF ptf[4];
2468     POINT pti[4];
2469
2470     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2471
2472     if(!graphics || !brush)
2473         return InvalidParameter;
2474
2475     if(graphics->busy)
2476         return ObjectBusy;
2477
2478     ptf[0].X = x;
2479     ptf[0].Y = y;
2480     ptf[1].X = x + width;
2481     ptf[1].Y = y;
2482     ptf[2].X = x + width;
2483     ptf[2].Y = y + height;
2484     ptf[3].X = x;
2485     ptf[3].Y = y + height;
2486
2487     save_state = SaveDC(graphics->hdc);
2488     EndPath(graphics->hdc);
2489     SelectObject(graphics->hdc, brush->gdibrush);
2490     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2491
2492     transform_and_round_points(graphics, pti, ptf, 4);
2493
2494     Polygon(graphics->hdc, pti, 4);
2495
2496     RestoreDC(graphics->hdc, save_state);
2497
2498     return Ok;
2499 }
2500
2501 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2502     INT count)
2503 {
2504     GpStatus ret;
2505     INT i;
2506
2507     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2508
2509     if(!rects)
2510         return InvalidParameter;
2511
2512     for(i = 0; i < count; i++){
2513         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2514         if(ret != Ok)   return ret;
2515     }
2516
2517     return Ok;
2518 }
2519
2520 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2521     INT count)
2522 {
2523     GpRectF *rectsF;
2524     GpStatus ret;
2525     INT i;
2526
2527     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2528
2529     if(!rects || count <= 0)
2530         return InvalidParameter;
2531
2532     rectsF = GdipAlloc(sizeof(GpRectF)*count);
2533     if(!rectsF)
2534         return OutOfMemory;
2535
2536     for(i = 0; i < count; i++){
2537         rectsF[i].X      = (REAL)rects[i].X;
2538         rectsF[i].Y      = (REAL)rects[i].Y;
2539         rectsF[i].X      = (REAL)rects[i].Width;
2540         rectsF[i].Height = (REAL)rects[i].Height;
2541     }
2542
2543     ret = GdipFillRectangles(graphics,brush,rectsF,count);
2544     GdipFree(rectsF);
2545
2546     return ret;
2547 }
2548
2549 /*****************************************************************************
2550  * GdipFillRegion [GDIPLUS.@]
2551  */
2552 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2553         GpRegion* region)
2554 {
2555     INT save_state;
2556     GpStatus status;
2557     HRGN hrgn;
2558
2559     TRACE("(%p, %p, %p)\n", graphics, brush, region);
2560
2561     if (!(graphics && brush && region))
2562         return InvalidParameter;
2563
2564     if(graphics->busy)
2565         return ObjectBusy;
2566
2567     status = GdipGetRegionHRgn(region, graphics, &hrgn);
2568     if(status != Ok)
2569         return status;
2570
2571     save_state = SaveDC(graphics->hdc);
2572     EndPath(graphics->hdc);
2573     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2574
2575     FillRgn(graphics->hdc, hrgn, brush->gdibrush);
2576
2577     RestoreDC(graphics->hdc, save_state);
2578
2579     DeleteObject(hrgn);
2580
2581     return Ok;
2582 }
2583
2584 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2585 {
2586     static int calls;
2587
2588     if(!graphics)
2589         return InvalidParameter;
2590
2591     if(graphics->busy)
2592         return ObjectBusy;
2593
2594     if(!(calls++))
2595         FIXME("not implemented\n");
2596
2597     return NotImplemented;
2598 }
2599
2600 /*****************************************************************************
2601  * GdipGetClipBounds [GDIPLUS.@]
2602  */
2603 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
2604 {
2605     TRACE("(%p, %p)\n", graphics, rect);
2606
2607     if(!graphics)
2608         return InvalidParameter;
2609
2610     if(graphics->busy)
2611         return ObjectBusy;
2612
2613     return GdipGetRegionBounds(graphics->clip, graphics, rect);
2614 }
2615
2616 /*****************************************************************************
2617  * GdipGetClipBoundsI [GDIPLUS.@]
2618  */
2619 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
2620 {
2621     TRACE("(%p, %p)\n", graphics, rect);
2622
2623     if(!graphics)
2624         return InvalidParameter;
2625
2626     if(graphics->busy)
2627         return ObjectBusy;
2628
2629     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
2630 }
2631
2632 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2633 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2634     CompositingMode *mode)
2635 {
2636     TRACE("(%p, %p)\n", graphics, mode);
2637
2638     if(!graphics || !mode)
2639         return InvalidParameter;
2640
2641     if(graphics->busy)
2642         return ObjectBusy;
2643
2644     *mode = graphics->compmode;
2645
2646     return Ok;
2647 }
2648
2649 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2650 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2651     CompositingQuality *quality)
2652 {
2653     TRACE("(%p, %p)\n", graphics, quality);
2654
2655     if(!graphics || !quality)
2656         return InvalidParameter;
2657
2658     if(graphics->busy)
2659         return ObjectBusy;
2660
2661     *quality = graphics->compqual;
2662
2663     return Ok;
2664 }
2665
2666 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2667 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2668     InterpolationMode *mode)
2669 {
2670     TRACE("(%p, %p)\n", graphics, mode);
2671
2672     if(!graphics || !mode)
2673         return InvalidParameter;
2674
2675     if(graphics->busy)
2676         return ObjectBusy;
2677
2678     *mode = graphics->interpolation;
2679
2680     return Ok;
2681 }
2682
2683 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2684 {
2685     if(!graphics || !argb)
2686         return InvalidParameter;
2687
2688     if(graphics->busy)
2689         return ObjectBusy;
2690
2691     FIXME("(%p, %p): stub\n", graphics, argb);
2692
2693     return NotImplemented;
2694 }
2695
2696 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2697 {
2698     TRACE("(%p, %p)\n", graphics, scale);
2699
2700     if(!graphics || !scale)
2701         return InvalidParameter;
2702
2703     if(graphics->busy)
2704         return ObjectBusy;
2705
2706     *scale = graphics->scale;
2707
2708     return Ok;
2709 }
2710
2711 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2712 {
2713     TRACE("(%p, %p)\n", graphics, unit);
2714
2715     if(!graphics || !unit)
2716         return InvalidParameter;
2717
2718     if(graphics->busy)
2719         return ObjectBusy;
2720
2721     *unit = graphics->unit;
2722
2723     return Ok;
2724 }
2725
2726 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2727 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2728     *mode)
2729 {
2730     TRACE("(%p, %p)\n", graphics, mode);
2731
2732     if(!graphics || !mode)
2733         return InvalidParameter;
2734
2735     if(graphics->busy)
2736         return ObjectBusy;
2737
2738     *mode = graphics->pixeloffset;
2739
2740     return Ok;
2741 }
2742
2743 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2744 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2745 {
2746     TRACE("(%p, %p)\n", graphics, mode);
2747
2748     if(!graphics || !mode)
2749         return InvalidParameter;
2750
2751     if(graphics->busy)
2752         return ObjectBusy;
2753
2754     *mode = graphics->smoothing;
2755
2756     return Ok;
2757 }
2758
2759 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2760 {
2761     TRACE("(%p, %p)\n", graphics, contrast);
2762
2763     if(!graphics || !contrast)
2764         return InvalidParameter;
2765
2766     *contrast = graphics->textcontrast;
2767
2768     return Ok;
2769 }
2770
2771 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2772 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2773     TextRenderingHint *hint)
2774 {
2775     TRACE("(%p, %p)\n", graphics, hint);
2776
2777     if(!graphics || !hint)
2778         return InvalidParameter;
2779
2780     if(graphics->busy)
2781         return ObjectBusy;
2782
2783     *hint = graphics->texthint;
2784
2785     return Ok;
2786 }
2787
2788 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2789 {
2790     TRACE("(%p, %p)\n", graphics, matrix);
2791
2792     if(!graphics || !matrix)
2793         return InvalidParameter;
2794
2795     if(graphics->busy)
2796         return ObjectBusy;
2797
2798     *matrix = *graphics->worldtrans;
2799     return Ok;
2800 }
2801
2802 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2803 {
2804     GpSolidFill *brush;
2805     GpStatus stat;
2806     RECT rect;
2807
2808     TRACE("(%p, %x)\n", graphics, color);
2809
2810     if(!graphics)
2811         return InvalidParameter;
2812
2813     if(graphics->busy)
2814         return ObjectBusy;
2815
2816     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2817         return stat;
2818
2819     if(graphics->hwnd){
2820         if(!GetWindowRect(graphics->hwnd, &rect)){
2821             GdipDeleteBrush((GpBrush*)brush);
2822             return GenericError;
2823         }
2824
2825         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right  - rect.left),
2826                                                                (REAL)(rect.bottom - rect.top));
2827     }
2828     else
2829         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2830                                                                (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2831
2832     GdipDeleteBrush((GpBrush*)brush);
2833
2834     return Ok;
2835 }
2836
2837 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2838 {
2839     TRACE("(%p, %p)\n", graphics, res);
2840
2841     if(!graphics || !res)
2842         return InvalidParameter;
2843
2844     return GdipIsEmptyRegion(graphics->clip, graphics, res);
2845 }
2846
2847 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2848 {
2849     FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2850
2851     if(!graphics || !result)
2852         return InvalidParameter;
2853
2854     if(graphics->busy)
2855         return ObjectBusy;
2856
2857     return NotImplemented;
2858 }
2859
2860 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2861 {
2862     FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2863
2864     if(!graphics || !result)
2865         return InvalidParameter;
2866
2867     if(graphics->busy)
2868         return ObjectBusy;
2869
2870     return NotImplemented;
2871 }
2872
2873 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2874         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2875         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2876         INT regionCount, GpRegion** regions)
2877 {
2878     if (!(graphics && string && font && layoutRect && stringFormat && regions))
2879         return InvalidParameter;
2880
2881     FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2882             length, font, layoutRect, stringFormat, regionCount, regions);
2883
2884     return NotImplemented;
2885 }
2886
2887 /* Find the smallest rectangle that bounds the text when it is printed in rect
2888  * according to the format options listed in format. If rect has 0 width and
2889  * height, then just find the smallest rectangle that bounds the text when it's
2890  * printed at location (rect->X, rect-Y). */
2891 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2892     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2893     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2894     INT *codepointsfitted, INT *linesfilled)
2895 {
2896     HFONT oldfont;
2897     WCHAR* stringdup;
2898     INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2899         nheight;
2900     SIZE size;
2901
2902     if(!graphics || !string || !font || !rect)
2903         return InvalidParameter;
2904
2905     if(linesfilled) *linesfilled = 0;
2906     if(codepointsfitted) *codepointsfitted = 0;
2907
2908     if(format)
2909         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2910
2911     if(length == -1) length = lstrlenW(string);
2912
2913     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2914     if(!stringdup) return OutOfMemory;
2915
2916     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2917     nwidth = roundr(rect->Width);
2918     nheight = roundr(rect->Height);
2919
2920     if((nwidth == 0) && (nheight == 0))
2921         nwidth = nheight = INT_MAX;
2922
2923     for(i = 0, j = 0; i < length; i++){
2924         if(!isprintW(string[i]) && (string[i] != '\n'))
2925             continue;
2926
2927         stringdup[j] = string[i];
2928         j++;
2929     }
2930
2931     stringdup[j] = 0;
2932     length = j;
2933
2934     while(sum < length){
2935         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2936                               nwidth, &fit, NULL, &size);
2937         fitcpy = fit;
2938
2939         if(fit == 0)
2940             break;
2941
2942         for(lret = 0; lret < fit; lret++)
2943             if(*(stringdup + sum + lret) == '\n')
2944                 break;
2945
2946         /* Line break code (may look strange, but it imitates windows). */
2947         if(lret < fit)
2948             fit = lret;    /* this is not an off-by-one error */
2949         else if(fit < (length - sum)){
2950             if(*(stringdup + sum + fit) == ' ')
2951                 while(*(stringdup + sum + fit) == ' ')
2952                     fit++;
2953             else
2954                 while(*(stringdup + sum + fit - 1) != ' '){
2955                     fit--;
2956
2957                     if(*(stringdup + sum + fit) == '\t')
2958                         break;
2959
2960                     if(fit == 0){
2961                         fit = fitcpy;
2962                         break;
2963                     }
2964                 }
2965         }
2966
2967         GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2968                               nwidth, &j, NULL, &size);
2969
2970         sum += fit + (lret < fitcpy ? 1 : 0);
2971         if(codepointsfitted) *codepointsfitted = sum;
2972
2973         height += size.cy;
2974         if(linesfilled) *linesfilled += size.cy;
2975         max_width = max(max_width, size.cx);
2976
2977         if(height > nheight)
2978             break;
2979
2980         /* Stop if this was a linewrap (but not if it was a linebreak). */
2981         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2982             break;
2983     }
2984
2985     bounds->X = rect->X;
2986     bounds->Y = rect->Y;
2987     bounds->Width = (REAL)max_width;
2988     bounds->Height = (REAL) min(height, nheight);
2989
2990     GdipFree(stringdup);
2991     DeleteObject(SelectObject(graphics->hdc, oldfont));
2992
2993     return Ok;
2994 }
2995
2996 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
2997 {
2998     TRACE("(%p)\n", graphics);
2999
3000     if(!graphics)
3001         return InvalidParameter;
3002
3003     if(graphics->busy)
3004         return ObjectBusy;
3005
3006     return GdipSetInfinite(graphics->clip);
3007 }
3008
3009 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3010 {
3011     TRACE("(%p)\n", graphics);
3012
3013     if(!graphics)
3014         return InvalidParameter;
3015
3016     if(graphics->busy)
3017         return ObjectBusy;
3018
3019     graphics->worldtrans->matrix[0] = 1.0;
3020     graphics->worldtrans->matrix[1] = 0.0;
3021     graphics->worldtrans->matrix[2] = 0.0;
3022     graphics->worldtrans->matrix[3] = 1.0;
3023     graphics->worldtrans->matrix[4] = 0.0;
3024     graphics->worldtrans->matrix[5] = 0.0;
3025
3026     return Ok;
3027 }
3028
3029 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3030 {
3031     static int calls;
3032
3033     if(!graphics)
3034         return InvalidParameter;
3035
3036     if(!(calls++))
3037         FIXME("graphics state not implemented\n");
3038
3039     return Ok;
3040 }
3041
3042 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3043     GpMatrixOrder order)
3044 {
3045     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3046
3047     if(!graphics)
3048         return InvalidParameter;
3049
3050     if(graphics->busy)
3051         return ObjectBusy;
3052
3053     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3054 }
3055
3056 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3057 {
3058     static int calls;
3059
3060     if(!graphics || !state)
3061         return InvalidParameter;
3062
3063     if(!(calls++))
3064         FIXME("graphics state not implemented\n");
3065
3066     *state = 0xdeadbeef;
3067     return Ok;
3068 }
3069
3070 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
3071 {
3072     FIXME("(%p, %p)\n", graphics, state);
3073
3074     if(!graphics || !state)
3075         return InvalidParameter;
3076
3077     *state = 0xdeadbeef;
3078     return Ok;
3079 }
3080
3081 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
3082 {
3083     FIXME("(%p, 0x%x)\n", graphics, state);
3084
3085     if(!graphics || !state)
3086         return InvalidParameter;
3087
3088     return Ok;
3089 }
3090
3091 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
3092     REAL sy, GpMatrixOrder order)
3093 {
3094     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
3095
3096     if(!graphics)
3097         return InvalidParameter;
3098
3099     if(graphics->busy)
3100         return ObjectBusy;
3101
3102     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
3103 }
3104
3105 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
3106     CombineMode mode)
3107 {
3108     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
3109
3110     if(!graphics || !srcgraphics)
3111         return InvalidParameter;
3112
3113     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
3114 }
3115
3116 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
3117     CompositingMode mode)
3118 {
3119     TRACE("(%p, %d)\n", graphics, mode);
3120
3121     if(!graphics)
3122         return InvalidParameter;
3123
3124     if(graphics->busy)
3125         return ObjectBusy;
3126
3127     graphics->compmode = mode;
3128
3129     return Ok;
3130 }
3131
3132 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
3133     CompositingQuality quality)
3134 {
3135     TRACE("(%p, %d)\n", graphics, quality);
3136
3137     if(!graphics)
3138         return InvalidParameter;
3139
3140     if(graphics->busy)
3141         return ObjectBusy;
3142
3143     graphics->compqual = quality;
3144
3145     return Ok;
3146 }
3147
3148 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
3149     InterpolationMode mode)
3150 {
3151     TRACE("(%p, %d)\n", graphics, mode);
3152
3153     if(!graphics)
3154         return InvalidParameter;
3155
3156     if(graphics->busy)
3157         return ObjectBusy;
3158
3159     graphics->interpolation = mode;
3160
3161     return Ok;
3162 }
3163
3164 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
3165 {
3166     TRACE("(%p, %.2f)\n", graphics, scale);
3167
3168     if(!graphics || (scale <= 0.0))
3169         return InvalidParameter;
3170
3171     if(graphics->busy)
3172         return ObjectBusy;
3173
3174     graphics->scale = scale;
3175
3176     return Ok;
3177 }
3178
3179 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
3180 {
3181     TRACE("(%p, %d)\n", graphics, unit);
3182
3183     if(!graphics)
3184         return InvalidParameter;
3185
3186     if(graphics->busy)
3187         return ObjectBusy;
3188
3189     if(unit == UnitWorld)
3190         return InvalidParameter;
3191
3192     graphics->unit = unit;
3193
3194     return Ok;
3195 }
3196
3197 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3198     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->pixeloffset = mode;
3209
3210     return Ok;
3211 }
3212
3213 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
3214 {
3215     TRACE("(%p, %d)\n", graphics, mode);
3216
3217     if(!graphics)
3218         return InvalidParameter;
3219
3220     if(graphics->busy)
3221         return ObjectBusy;
3222
3223     graphics->smoothing = mode;
3224
3225     return Ok;
3226 }
3227
3228 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3229 {
3230     TRACE("(%p, %d)\n", graphics, contrast);
3231
3232     if(!graphics)
3233         return InvalidParameter;
3234
3235     graphics->textcontrast = contrast;
3236
3237     return Ok;
3238 }
3239
3240 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3241     TextRenderingHint hint)
3242 {
3243     TRACE("(%p, %d)\n", graphics, hint);
3244
3245     if(!graphics)
3246         return InvalidParameter;
3247
3248     if(graphics->busy)
3249         return ObjectBusy;
3250
3251     graphics->texthint = hint;
3252
3253     return Ok;
3254 }
3255
3256 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3257 {
3258     TRACE("(%p, %p)\n", graphics, matrix);
3259
3260     if(!graphics || !matrix)
3261         return InvalidParameter;
3262
3263     if(graphics->busy)
3264         return ObjectBusy;
3265
3266     GdipDeleteMatrix(graphics->worldtrans);
3267     return GdipCloneMatrix(matrix, &graphics->worldtrans);
3268 }
3269
3270 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3271     REAL dy, GpMatrixOrder order)
3272 {
3273     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3274
3275     if(!graphics)
3276         return InvalidParameter;
3277
3278     if(graphics->busy)
3279         return ObjectBusy;
3280
3281     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3282 }
3283
3284 /*****************************************************************************
3285  * GdipSetClipHrgn [GDIPLUS.@]
3286  */
3287 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
3288 {
3289     GpRegion *region;
3290     GpStatus status;
3291
3292     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
3293
3294     if(!graphics)
3295         return InvalidParameter;
3296
3297     status = GdipCreateRegionHrgn(hrgn, &region);
3298     if(status != Ok)
3299         return status;
3300
3301     status = GdipSetClipRegion(graphics, region, mode);
3302
3303     GdipDeleteRegion(region);
3304     return status;
3305 }
3306
3307 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3308 {
3309     TRACE("(%p, %p, %d)\n", graphics, path, mode);
3310
3311     if(!graphics)
3312         return InvalidParameter;
3313
3314     if(graphics->busy)
3315         return ObjectBusy;
3316
3317     return GdipCombineRegionPath(graphics->clip, path, mode);
3318 }
3319
3320 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3321                                     REAL width, REAL height,
3322                                     CombineMode mode)
3323 {
3324     GpRectF rect;
3325
3326     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3327
3328     if(!graphics)
3329         return InvalidParameter;
3330
3331     if(graphics->busy)
3332         return ObjectBusy;
3333
3334     rect.X = x;
3335     rect.Y = y;
3336     rect.Width  = width;
3337     rect.Height = height;
3338
3339     return GdipCombineRegionRect(graphics->clip, &rect, mode);
3340 }
3341
3342 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3343                                      INT width, INT height,
3344                                      CombineMode mode)
3345 {
3346     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3347
3348     if(!graphics)
3349         return InvalidParameter;
3350
3351     if(graphics->busy)
3352         return ObjectBusy;
3353
3354     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3355 }
3356
3357 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3358                                       CombineMode mode)
3359 {
3360     TRACE("(%p, %p, %d)\n", graphics, region, mode);
3361
3362     if(!graphics || !region)
3363         return InvalidParameter;
3364
3365     if(graphics->busy)
3366         return ObjectBusy;
3367
3368     return GdipCombineRegionRegion(graphics->clip, region, mode);
3369 }
3370
3371 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3372     UINT limitDpi)
3373 {
3374     static int calls;
3375
3376     if(!(calls++))
3377         FIXME("not implemented\n");
3378
3379     return NotImplemented;
3380 }
3381
3382 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3383     INT count)
3384 {
3385     INT save_state;
3386     POINT *pti;
3387
3388     TRACE("(%p, %p, %d)\n", graphics, points, count);
3389
3390     if(!graphics || !pen || count<=0)
3391         return InvalidParameter;
3392
3393     if(graphics->busy)
3394         return ObjectBusy;
3395
3396     pti = GdipAlloc(sizeof(POINT) * count);
3397
3398     save_state = prepare_dc(graphics, pen);
3399     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3400
3401     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3402     Polygon(graphics->hdc, pti, count);
3403
3404     restore_dc(graphics, save_state);
3405     GdipFree(pti);
3406
3407     return Ok;
3408 }
3409
3410 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3411     INT count)
3412 {
3413     GpStatus ret;
3414     GpPointF *ptf;
3415     INT i;
3416
3417     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3418
3419     if(count<=0)    return InvalidParameter;
3420     ptf = GdipAlloc(sizeof(GpPointF) * count);
3421
3422     for(i = 0;i < count; i++){
3423         ptf[i].X = (REAL)points[i].X;
3424         ptf[i].Y = (REAL)points[i].Y;
3425     }
3426
3427     ret = GdipDrawPolygon(graphics,pen,ptf,count);
3428     GdipFree(ptf);
3429
3430     return ret;
3431 }
3432
3433 GpStatus WINGDIPAPI GdipGetDpiX(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, LOGPIXELSX);
3444
3445     return Ok;
3446 }
3447
3448 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3449 {
3450     TRACE("(%p, %p)\n", graphics, dpi);
3451
3452     if(!graphics || !dpi)
3453         return InvalidParameter;
3454
3455     if(graphics->busy)
3456         return ObjectBusy;
3457
3458     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3459
3460     return Ok;
3461 }
3462
3463 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3464     GpMatrixOrder order)
3465 {
3466     GpMatrix m;
3467     GpStatus ret;
3468
3469     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3470
3471     if(!graphics || !matrix)
3472         return InvalidParameter;
3473
3474     if(graphics->busy)
3475         return ObjectBusy;
3476
3477     m = *(graphics->worldtrans);
3478
3479     ret = GdipMultiplyMatrix(&m, matrix, order);
3480     if(ret == Ok)
3481         *(graphics->worldtrans) = m;
3482
3483     return ret;
3484 }
3485
3486 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3487 {
3488     TRACE("(%p, %p)\n", graphics, hdc);
3489
3490     if(!graphics || !hdc)
3491         return InvalidParameter;
3492
3493     if(graphics->busy)
3494         return ObjectBusy;
3495
3496     *hdc = graphics->hdc;
3497     graphics->busy = TRUE;
3498
3499     return Ok;
3500 }
3501
3502 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3503 {
3504     TRACE("(%p, %p)\n", graphics, hdc);
3505
3506     if(!graphics)
3507         return InvalidParameter;
3508
3509     if(graphics->hdc != hdc || !(graphics->busy))
3510         return InvalidParameter;
3511
3512     graphics->busy = FALSE;
3513
3514     return Ok;
3515 }
3516
3517 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3518 {
3519     GpRegion *clip;
3520     GpStatus status;
3521
3522     TRACE("(%p, %p)\n", graphics, region);
3523
3524     if(!graphics || !region)
3525         return InvalidParameter;
3526
3527     if(graphics->busy)
3528         return ObjectBusy;
3529
3530     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3531         return status;
3532
3533     /* free everything except root node and header */
3534     delete_element(&region->node);
3535     memcpy(region, clip, sizeof(GpRegion));
3536
3537     return Ok;
3538 }
3539
3540 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3541                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
3542 {
3543     if(!graphics || !points || count <= 0)
3544         return InvalidParameter;
3545
3546     if(graphics->busy)
3547         return ObjectBusy;
3548
3549     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3550
3551     return NotImplemented;
3552 }
3553
3554 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3555                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
3556 {
3557     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3558
3559     return NotImplemented;
3560 }
3561
3562 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3563 {
3564     FIXME("\n");
3565
3566     return NULL;
3567 }
3568
3569 /*****************************************************************************
3570  * GdipTranslateClip [GDIPLUS.@]
3571  */
3572 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
3573 {
3574     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
3575
3576     if(!graphics)
3577         return InvalidParameter;
3578
3579     if(graphics->busy)
3580         return ObjectBusy;
3581
3582     return GdipTranslateRegion(graphics->clip, dx, dy);
3583 }
3584
3585 /*****************************************************************************
3586  * GdipTranslateClipI [GDIPLUS.@]
3587  */
3588 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
3589 {
3590     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
3591
3592     if(!graphics)
3593         return InvalidParameter;
3594
3595     if(graphics->busy)
3596         return ObjectBusy;
3597
3598     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
3599 }