wined3d: Handle the sampler type shift in the frontend.
[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)->owndc = FALSE;
928     (*graphics)->smoothing = SmoothingModeDefault;
929     (*graphics)->compqual = CompositingQualityDefault;
930     (*graphics)->interpolation = InterpolationModeDefault;
931     (*graphics)->pixeloffset = PixelOffsetModeDefault;
932     (*graphics)->compmode = CompositingModeSourceOver;
933     (*graphics)->unit = UnitDisplay;
934     (*graphics)->scale = 1.0;
935     (*graphics)->busy = FALSE;
936     (*graphics)->textcontrast = 4;
937
938     return Ok;
939 }
940
941 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
942 {
943     GpStatus ret;
944     HDC hdc;
945
946     TRACE("(%p, %p)\n", hwnd, graphics);
947
948     hdc = GetDC(hwnd);
949
950     if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
951     {
952         ReleaseDC(hwnd, hdc);
953         return ret;
954     }
955
956     (*graphics)->hwnd = hwnd;
957     (*graphics)->owndc = TRUE;
958
959     return Ok;
960 }
961
962 /* FIXME: no icm handling */
963 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
964 {
965     TRACE("(%p, %p)\n", hwnd, graphics);
966
967     return GdipCreateFromHWND(hwnd, graphics);
968 }
969
970 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
971     GpMetafile **metafile)
972 {
973     static int calls;
974
975     if(!hemf || !metafile)
976         return InvalidParameter;
977
978     if(!(calls++))
979         FIXME("not implemented\n");
980
981     return NotImplemented;
982 }
983
984 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
985     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
986 {
987     IStream *stream = NULL;
988     UINT read;
989     BYTE* copy;
990     HENHMETAFILE hemf;
991     GpStatus retval = GenericError;
992
993     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
994
995     if(!hwmf || !metafile || !placeable)
996         return InvalidParameter;
997
998     *metafile = NULL;
999     read = GetMetaFileBitsEx(hwmf, 0, NULL);
1000     if(!read)
1001         return GenericError;
1002     copy = GdipAlloc(read);
1003     GetMetaFileBitsEx(hwmf, read, copy);
1004
1005     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1006     GdipFree(copy);
1007
1008     read = GetEnhMetaFileBits(hemf, 0, NULL);
1009     copy = GdipAlloc(read);
1010     GetEnhMetaFileBits(hemf, read, copy);
1011     DeleteEnhMetaFile(hemf);
1012
1013     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
1014         ERR("could not make stream\n");
1015         GdipFree(copy);
1016         goto err;
1017     }
1018
1019     *metafile = GdipAlloc(sizeof(GpMetafile));
1020     if(!*metafile){
1021         retval = OutOfMemory;
1022         goto err;
1023     }
1024
1025     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
1026         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
1027         goto err;
1028
1029
1030     (*metafile)->image.type = ImageTypeMetafile;
1031     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
1032     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Right) / ((REAL) placeable->Inch);
1033     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
1034                     - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
1035     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
1036                    - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
1037     (*metafile)->unit = UnitInch;
1038
1039     if(delete)
1040         DeleteMetaFile(hwmf);
1041
1042     return Ok;
1043
1044 err:
1045     GdipFree(*metafile);
1046     IStream_Release(stream);
1047     return retval;
1048 }
1049
1050 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1051     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1052 {
1053     HMETAFILE hmf = GetMetaFileW(file);
1054
1055     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1056
1057     if(!hmf) return InvalidParameter;
1058
1059     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1060 }
1061
1062 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
1063     UINT access, IStream **stream)
1064 {
1065     DWORD dwMode;
1066     HRESULT ret;
1067
1068     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
1069
1070     if(!stream || !filename)
1071         return InvalidParameter;
1072
1073     if(access & GENERIC_WRITE)
1074         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
1075     else if(access & GENERIC_READ)
1076         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
1077     else
1078         return InvalidParameter;
1079
1080     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
1081
1082     return hresult_to_status(ret);
1083 }
1084
1085 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
1086 {
1087     TRACE("(%p)\n", graphics);
1088
1089     if(!graphics) return InvalidParameter;
1090     if(graphics->busy) return ObjectBusy;
1091
1092     if(graphics->owndc)
1093         ReleaseDC(graphics->hwnd, graphics->hdc);
1094
1095     GdipDeleteRegion(graphics->clip);
1096     GdipDeleteMatrix(graphics->worldtrans);
1097     GdipFree(graphics);
1098
1099     return Ok;
1100 }
1101
1102 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
1103     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1104 {
1105     INT save_state, num_pts;
1106     GpPointF points[MAX_ARC_PTS];
1107     GpStatus retval;
1108
1109     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1110           width, height, startAngle, sweepAngle);
1111
1112     if(!graphics || !pen || width <= 0 || height <= 0)
1113         return InvalidParameter;
1114
1115     if(graphics->busy)
1116         return ObjectBusy;
1117
1118     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
1119
1120     save_state = prepare_dc(graphics, pen);
1121
1122     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
1123
1124     restore_dc(graphics, save_state);
1125
1126     return retval;
1127 }
1128
1129 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
1130     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1131 {
1132     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1133           width, height, startAngle, sweepAngle);
1134
1135     return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1136 }
1137
1138 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
1139     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
1140 {
1141     INT save_state;
1142     GpPointF pt[4];
1143     GpStatus retval;
1144
1145     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
1146           x2, y2, x3, y3, x4, y4);
1147
1148     if(!graphics || !pen)
1149         return InvalidParameter;
1150
1151     if(graphics->busy)
1152         return ObjectBusy;
1153
1154     pt[0].X = x1;
1155     pt[0].Y = y1;
1156     pt[1].X = x2;
1157     pt[1].Y = y2;
1158     pt[2].X = x3;
1159     pt[2].Y = y3;
1160     pt[3].X = x4;
1161     pt[3].Y = y4;
1162
1163     save_state = prepare_dc(graphics, pen);
1164
1165     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1166
1167     restore_dc(graphics, save_state);
1168
1169     return retval;
1170 }
1171
1172 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
1173     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
1174 {
1175     INT save_state;
1176     GpPointF pt[4];
1177     GpStatus retval;
1178
1179     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1180           x2, y2, x3, y3, x4, y4);
1181
1182     if(!graphics || !pen)
1183         return InvalidParameter;
1184
1185     if(graphics->busy)
1186         return ObjectBusy;
1187
1188     pt[0].X = x1;
1189     pt[0].Y = y1;
1190     pt[1].X = x2;
1191     pt[1].Y = y2;
1192     pt[2].X = x3;
1193     pt[2].Y = y3;
1194     pt[3].X = x4;
1195     pt[3].Y = y4;
1196
1197     save_state = prepare_dc(graphics, pen);
1198
1199     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1200
1201     restore_dc(graphics, save_state);
1202
1203     return retval;
1204 }
1205
1206 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1207     GDIPCONST GpPointF *points, INT count)
1208 {
1209     INT i;
1210     GpStatus ret;
1211
1212     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1213
1214     if(!graphics || !pen || !points || (count <= 0))
1215         return InvalidParameter;
1216
1217     if(graphics->busy)
1218         return ObjectBusy;
1219
1220     for(i = 0; i < floor(count / 4); i++){
1221         ret = GdipDrawBezier(graphics, pen,
1222                              points[4*i].X, points[4*i].Y,
1223                              points[4*i + 1].X, points[4*i + 1].Y,
1224                              points[4*i + 2].X, points[4*i + 2].Y,
1225                              points[4*i + 3].X, points[4*i + 3].Y);
1226         if(ret != Ok)
1227             return ret;
1228     }
1229
1230     return Ok;
1231 }
1232
1233 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1234     GDIPCONST GpPoint *points, INT count)
1235 {
1236     GpPointF *pts;
1237     GpStatus ret;
1238     INT i;
1239
1240     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1241
1242     if(!graphics || !pen || !points || (count <= 0))
1243         return InvalidParameter;
1244
1245     if(graphics->busy)
1246         return ObjectBusy;
1247
1248     pts = GdipAlloc(sizeof(GpPointF) * count);
1249     if(!pts)
1250         return OutOfMemory;
1251
1252     for(i = 0; i < count; i++){
1253         pts[i].X = (REAL)points[i].X;
1254         pts[i].Y = (REAL)points[i].Y;
1255     }
1256
1257     ret = GdipDrawBeziers(graphics,pen,pts,count);
1258
1259     GdipFree(pts);
1260
1261     return ret;
1262 }
1263
1264 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1265     GDIPCONST GpPointF *points, INT count)
1266 {
1267     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1268
1269     return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1270 }
1271
1272 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1273     GDIPCONST GpPoint *points, INT count)
1274 {
1275     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1276
1277     return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1278 }
1279
1280 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1281     GDIPCONST GpPointF *points, INT count, REAL tension)
1282 {
1283     GpPath *path;
1284     GpStatus stat;
1285
1286     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1287
1288     if(!graphics || !pen || !points || count <= 0)
1289         return InvalidParameter;
1290
1291     if(graphics->busy)
1292         return ObjectBusy;
1293
1294     if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1295         return stat;
1296
1297     stat = GdipAddPathClosedCurve2(path, points, count, tension);
1298     if(stat != Ok){
1299         GdipDeletePath(path);
1300         return stat;
1301     }
1302
1303     stat = GdipDrawPath(graphics, pen, path);
1304
1305     GdipDeletePath(path);
1306
1307     return stat;
1308 }
1309
1310 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1311     GDIPCONST GpPoint *points, INT count, REAL tension)
1312 {
1313     GpPointF *ptf;
1314     GpStatus stat;
1315     INT i;
1316
1317     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1318
1319     if(!points || count <= 0)
1320         return InvalidParameter;
1321
1322     ptf = GdipAlloc(sizeof(GpPointF)*count);
1323     if(!ptf)
1324         return OutOfMemory;
1325
1326     for(i = 0; i < count; i++){
1327         ptf[i].X = (REAL)points[i].X;
1328         ptf[i].Y = (REAL)points[i].Y;
1329     }
1330
1331     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1332
1333     GdipFree(ptf);
1334
1335     return stat;
1336 }
1337
1338 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1339     GDIPCONST GpPointF *points, INT count)
1340 {
1341     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1342
1343     return GdipDrawCurve2(graphics,pen,points,count,1.0);
1344 }
1345
1346 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1347     GDIPCONST GpPoint *points, INT count)
1348 {
1349     GpPointF *pointsF;
1350     GpStatus ret;
1351     INT i;
1352
1353     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1354
1355     if(!points || count <= 0)
1356         return InvalidParameter;
1357
1358     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1359     if(!pointsF)
1360         return OutOfMemory;
1361
1362     for(i = 0; i < count; i++){
1363         pointsF[i].X = (REAL)points[i].X;
1364         pointsF[i].Y = (REAL)points[i].Y;
1365     }
1366
1367     ret = GdipDrawCurve(graphics,pen,pointsF,count);
1368     GdipFree(pointsF);
1369
1370     return ret;
1371 }
1372
1373 /* Approximates cardinal spline with Bezier curves. */
1374 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1375     GDIPCONST GpPointF *points, INT count, REAL tension)
1376 {
1377     /* PolyBezier expects count*3-2 points. */
1378     INT i, len_pt = count*3-2, save_state;
1379     GpPointF *pt;
1380     REAL x1, x2, y1, y2;
1381     GpStatus retval;
1382
1383     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1384
1385     if(!graphics || !pen)
1386         return InvalidParameter;
1387
1388     if(graphics->busy)
1389         return ObjectBusy;
1390
1391     pt = GdipAlloc(len_pt * sizeof(GpPointF));
1392     tension = tension * TENSION_CONST;
1393
1394     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1395         tension, &x1, &y1);
1396
1397     pt[0].X = points[0].X;
1398     pt[0].Y = points[0].Y;
1399     pt[1].X = x1;
1400     pt[1].Y = y1;
1401
1402     for(i = 0; i < count-2; i++){
1403         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1404
1405         pt[3*i+2].X = x1;
1406         pt[3*i+2].Y = y1;
1407         pt[3*i+3].X = points[i+1].X;
1408         pt[3*i+3].Y = points[i+1].Y;
1409         pt[3*i+4].X = x2;
1410         pt[3*i+4].Y = y2;
1411     }
1412
1413     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
1414         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
1415
1416     pt[len_pt-2].X = x1;
1417     pt[len_pt-2].Y = y1;
1418     pt[len_pt-1].X = points[count-1].X;
1419     pt[len_pt-1].Y = points[count-1].Y;
1420
1421     save_state = prepare_dc(graphics, pen);
1422
1423     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1424
1425     GdipFree(pt);
1426     restore_dc(graphics, save_state);
1427
1428     return retval;
1429 }
1430
1431 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1432     GDIPCONST GpPoint *points, INT count, REAL tension)
1433 {
1434     GpPointF *pointsF;
1435     GpStatus ret;
1436     INT i;
1437
1438     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1439
1440     if(!points || count <= 0)
1441         return InvalidParameter;
1442
1443     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1444     if(!pointsF)
1445         return OutOfMemory;
1446
1447     for(i = 0; i < count; i++){
1448         pointsF[i].X = (REAL)points[i].X;
1449         pointsF[i].Y = (REAL)points[i].Y;
1450     }
1451
1452     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1453     GdipFree(pointsF);
1454
1455     return ret;
1456 }
1457
1458 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1459     REAL y, REAL width, REAL height)
1460 {
1461     INT save_state;
1462     GpPointF ptf[2];
1463     POINT pti[2];
1464
1465     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1466
1467     if(!graphics || !pen)
1468         return InvalidParameter;
1469
1470     if(graphics->busy)
1471         return ObjectBusy;
1472
1473     ptf[0].X = x;
1474     ptf[0].Y = y;
1475     ptf[1].X = x + width;
1476     ptf[1].Y = y + height;
1477
1478     save_state = prepare_dc(graphics, pen);
1479     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1480
1481     transform_and_round_points(graphics, pti, ptf, 2);
1482
1483     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1484
1485     restore_dc(graphics, save_state);
1486
1487     return Ok;
1488 }
1489
1490 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1491     INT y, INT width, INT height)
1492 {
1493     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1494
1495     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1496 }
1497
1498
1499 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1500 {
1501     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1502
1503     /* IPicture::Render uses LONG coords */
1504     return GdipDrawImageI(graphics,image,roundr(x),roundr(y));
1505 }
1506
1507 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1508     INT y)
1509 {
1510     UINT width, height, srcw, srch;
1511
1512     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1513
1514     if(!graphics || !image)
1515         return InvalidParameter;
1516
1517     GdipGetImageWidth(image, &width);
1518     GdipGetImageHeight(image, &height);
1519
1520     srcw = width * (((REAL) INCH_HIMETRIC) /
1521             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1522     srch = height * (((REAL) INCH_HIMETRIC) /
1523             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1524
1525     if(image->type != ImageTypeMetafile){
1526         y += height;
1527         height *= -1;
1528     }
1529
1530     IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1531                     0, 0, srcw, srch, NULL);
1532
1533     return Ok;
1534 }
1535
1536 /* FIXME: partially implemented (only works for rectangular parallelograms) */
1537 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1538      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1539      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1540      DrawImageAbort callback, VOID * callbackData)
1541 {
1542     GpPointF ptf[3];
1543     POINT pti[3];
1544     REAL dx, dy;
1545
1546     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1547           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1548           callbackData);
1549
1550     if(!graphics || !image || !points || count != 3)
1551          return InvalidParameter;
1552
1553     if(srcUnit == UnitInch)
1554         dx = dy = (REAL) INCH_HIMETRIC;
1555     else if(srcUnit == UnitPixel){
1556         dx = ((REAL) INCH_HIMETRIC) /
1557              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1558         dy = ((REAL) INCH_HIMETRIC) /
1559              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1560     }
1561     else
1562         return NotImplemented;
1563
1564     memcpy(ptf, points, 3 * sizeof(GpPointF));
1565     transform_and_round_points(graphics, pti, ptf, 3);
1566
1567     /* IPicture renders bitmaps with the y-axis reversed
1568      * FIXME: flipping for unknown image type might not be correct. */
1569     if(image->type != ImageTypeMetafile){
1570         INT temp;
1571         temp = pti[0].y;
1572         pti[0].y = pti[2].y;
1573         pti[2].y = temp;
1574     }
1575
1576     if(IPicture_Render(image->picture, graphics->hdc,
1577         pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1578         srcx * dx, srcy * dy,
1579         srcwidth * dx, srcheight * dy,
1580         NULL) != S_OK){
1581         if(callback)
1582             callback(callbackData);
1583         return GenericError;
1584     }
1585
1586     return Ok;
1587 }
1588
1589 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
1590      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
1591      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1592      DrawImageAbort callback, VOID * callbackData)
1593 {
1594     GpPointF pointsF[3];
1595     INT i;
1596
1597     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
1598           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1599           callbackData);
1600
1601     if(!points || count!=3)
1602         return InvalidParameter;
1603
1604     for(i = 0; i < count; i++){
1605         pointsF[i].X = (REAL)points[i].X;
1606         pointsF[i].Y = (REAL)points[i].Y;
1607     }
1608
1609     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
1610                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
1611                                    callback, callbackData);
1612 }
1613
1614 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
1615     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
1616     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
1617     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
1618     VOID * callbackData)
1619 {
1620     GpPointF points[3];
1621
1622     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
1623           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1624           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1625
1626     points[0].X = dstx;
1627     points[0].Y = dsty;
1628     points[1].X = dstx + dstwidth;
1629     points[1].Y = dsty;
1630     points[2].X = dstx;
1631     points[2].Y = dsty + dstheight;
1632
1633     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1634                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1635 }
1636
1637 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
1638         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
1639         INT srcwidth, INT srcheight, GpUnit srcUnit,
1640         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
1641         VOID * callbackData)
1642 {
1643     GpPointF points[3];
1644
1645     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
1646           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1647           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1648
1649     points[0].X = dstx;
1650     points[0].Y = dsty;
1651     points[1].X = dstx + dstwidth;
1652     points[1].Y = dsty;
1653     points[2].X = dstx;
1654     points[2].Y = dsty + dstheight;
1655
1656     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1657                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1658 }
1659
1660 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
1661     REAL x, REAL y, REAL width, REAL height)
1662 {
1663     RectF bounds;
1664     GpUnit unit;
1665     GpStatus ret;
1666
1667     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
1668
1669     if(!graphics || !image)
1670         return InvalidParameter;
1671
1672     ret = GdipGetImageBounds(image, &bounds, &unit);
1673     if(ret != Ok)
1674         return ret;
1675
1676     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
1677                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
1678                                  unit, NULL, NULL, NULL);
1679 }
1680
1681 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
1682     INT x, INT y, INT width, INT height)
1683 {
1684     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
1685
1686     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
1687 }
1688
1689 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1690     REAL y1, REAL x2, REAL y2)
1691 {
1692     INT save_state;
1693     GpPointF pt[2];
1694     GpStatus retval;
1695
1696     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
1697
1698     if(!pen || !graphics)
1699         return InvalidParameter;
1700
1701     if(graphics->busy)
1702         return ObjectBusy;
1703
1704     pt[0].X = x1;
1705     pt[0].Y = y1;
1706     pt[1].X = x2;
1707     pt[1].Y = y2;
1708
1709     save_state = prepare_dc(graphics, pen);
1710
1711     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1712
1713     restore_dc(graphics, save_state);
1714
1715     return retval;
1716 }
1717
1718 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1719     INT y1, INT x2, INT y2)
1720 {
1721     INT save_state;
1722     GpPointF pt[2];
1723     GpStatus retval;
1724
1725     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
1726
1727     if(!pen || !graphics)
1728         return InvalidParameter;
1729
1730     if(graphics->busy)
1731         return ObjectBusy;
1732
1733     pt[0].X = (REAL)x1;
1734     pt[0].Y = (REAL)y1;
1735     pt[1].X = (REAL)x2;
1736     pt[1].Y = (REAL)y2;
1737
1738     save_state = prepare_dc(graphics, pen);
1739
1740     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1741
1742     restore_dc(graphics, save_state);
1743
1744     return retval;
1745 }
1746
1747 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1748     GpPointF *points, INT count)
1749 {
1750     INT save_state;
1751     GpStatus retval;
1752
1753     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1754
1755     if(!pen || !graphics || (count < 2))
1756         return InvalidParameter;
1757
1758     if(graphics->busy)
1759         return ObjectBusy;
1760
1761     save_state = prepare_dc(graphics, pen);
1762
1763     retval = draw_polyline(graphics, pen, points, count, TRUE);
1764
1765     restore_dc(graphics, save_state);
1766
1767     return retval;
1768 }
1769
1770 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
1771     GpPoint *points, INT count)
1772 {
1773     INT save_state;
1774     GpStatus retval;
1775     GpPointF *ptf = NULL;
1776     int i;
1777
1778     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1779
1780     if(!pen || !graphics || (count < 2))
1781         return InvalidParameter;
1782
1783     if(graphics->busy)
1784         return ObjectBusy;
1785
1786     ptf = GdipAlloc(count * sizeof(GpPointF));
1787     if(!ptf) return OutOfMemory;
1788
1789     for(i = 0; i < count; i ++){
1790         ptf[i].X = (REAL) points[i].X;
1791         ptf[i].Y = (REAL) points[i].Y;
1792     }
1793
1794     save_state = prepare_dc(graphics, pen);
1795
1796     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
1797
1798     restore_dc(graphics, save_state);
1799
1800     GdipFree(ptf);
1801     return retval;
1802 }
1803
1804 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1805 {
1806     INT save_state;
1807     GpStatus retval;
1808
1809     TRACE("(%p, %p, %p)\n", graphics, pen, path);
1810
1811     if(!pen || !graphics)
1812         return InvalidParameter;
1813
1814     if(graphics->busy)
1815         return ObjectBusy;
1816
1817     save_state = prepare_dc(graphics, pen);
1818
1819     retval = draw_poly(graphics, pen, path->pathdata.Points,
1820                        path->pathdata.Types, path->pathdata.Count, TRUE);
1821
1822     restore_dc(graphics, save_state);
1823
1824     return retval;
1825 }
1826
1827 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1828     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1829 {
1830     INT save_state;
1831
1832     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1833             width, height, startAngle, sweepAngle);
1834
1835     if(!graphics || !pen)
1836         return InvalidParameter;
1837
1838     if(graphics->busy)
1839         return ObjectBusy;
1840
1841     save_state = prepare_dc(graphics, pen);
1842     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1843
1844     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1845
1846     restore_dc(graphics, save_state);
1847
1848     return Ok;
1849 }
1850
1851 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
1852     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1853 {
1854     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1855             width, height, startAngle, sweepAngle);
1856
1857     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1858 }
1859
1860 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
1861     REAL y, REAL width, REAL height)
1862 {
1863     INT save_state;
1864     GpPointF ptf[4];
1865     POINT pti[4];
1866
1867     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1868
1869     if(!pen || !graphics)
1870         return InvalidParameter;
1871
1872     if(graphics->busy)
1873         return ObjectBusy;
1874
1875     ptf[0].X = x;
1876     ptf[0].Y = y;
1877     ptf[1].X = x + width;
1878     ptf[1].Y = y;
1879     ptf[2].X = x + width;
1880     ptf[2].Y = y + height;
1881     ptf[3].X = x;
1882     ptf[3].Y = y + height;
1883
1884     save_state = prepare_dc(graphics, pen);
1885     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1886
1887     transform_and_round_points(graphics, pti, ptf, 4);
1888     Polygon(graphics->hdc, pti, 4);
1889
1890     restore_dc(graphics, save_state);
1891
1892     return Ok;
1893 }
1894
1895 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
1896     INT y, INT width, INT height)
1897 {
1898     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1899
1900     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1901 }
1902
1903 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
1904     GDIPCONST GpRectF* rects, INT count)
1905 {
1906     GpPointF *ptf;
1907     POINT *pti;
1908     INT save_state, i;
1909
1910     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1911
1912     if(!graphics || !pen || !rects || count < 1)
1913         return InvalidParameter;
1914
1915     if(graphics->busy)
1916         return ObjectBusy;
1917
1918     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
1919     pti = GdipAlloc(4 * count * sizeof(POINT));
1920
1921     if(!ptf || !pti){
1922         GdipFree(ptf);
1923         GdipFree(pti);
1924         return OutOfMemory;
1925     }
1926
1927     for(i = 0; i < count; i++){
1928         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
1929         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
1930         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
1931         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
1932     }
1933
1934     save_state = prepare_dc(graphics, pen);
1935     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1936
1937     transform_and_round_points(graphics, pti, ptf, 4 * count);
1938
1939     for(i = 0; i < count; i++)
1940         Polygon(graphics->hdc, &pti[4 * i], 4);
1941
1942     restore_dc(graphics, save_state);
1943
1944     GdipFree(ptf);
1945     GdipFree(pti);
1946
1947     return Ok;
1948 }
1949
1950 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
1951     GDIPCONST GpRect* rects, INT count)
1952 {
1953     GpRectF *rectsF;
1954     GpStatus ret;
1955     INT i;
1956
1957     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1958
1959     if(!rects || count<=0)
1960         return InvalidParameter;
1961
1962     rectsF = GdipAlloc(sizeof(GpRectF) * count);
1963     if(!rectsF)
1964         return OutOfMemory;
1965
1966     for(i = 0;i < count;i++){
1967         rectsF[i].X      = (REAL)rects[i].X;
1968         rectsF[i].Y      = (REAL)rects[i].Y;
1969         rectsF[i].Width  = (REAL)rects[i].Width;
1970         rectsF[i].Height = (REAL)rects[i].Height;
1971     }
1972
1973     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
1974     GdipFree(rectsF);
1975
1976     return ret;
1977 }
1978
1979 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
1980     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
1981     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
1982 {
1983     HRGN rgn = NULL;
1984     HFONT gdifont;
1985     LOGFONTW lfw;
1986     TEXTMETRICW textmet;
1987     GpPointF pt[2], rectcpy[4];
1988     POINT corners[4];
1989     WCHAR* stringdup;
1990     REAL angle, ang_cos, ang_sin, rel_width, rel_height;
1991     INT sum = 0, height = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
1992         nheight;
1993     SIZE size;
1994     RECT drawcoord;
1995
1996     if(!graphics || !string || !font || !brush || !rect)
1997         return InvalidParameter;
1998
1999     if((brush->bt != BrushTypeSolidColor)){
2000         FIXME("not implemented for given parameters\n");
2001         return NotImplemented;
2002     }
2003
2004     if(format)
2005         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2006
2007     if(length == -1) length = lstrlenW(string);
2008
2009     stringdup = GdipAlloc(length * sizeof(WCHAR));
2010     if(!stringdup) return OutOfMemory;
2011
2012     save_state = SaveDC(graphics->hdc);
2013     SetBkMode(graphics->hdc, TRANSPARENT);
2014     SetTextColor(graphics->hdc, brush->lb.lbColor);
2015
2016     rectcpy[3].X = rectcpy[0].X = rect->X;
2017     rectcpy[1].Y = rectcpy[0].Y = rect->Y;
2018     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
2019     rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
2020     transform_and_round_points(graphics, corners, rectcpy, 4);
2021
2022     if (roundr(rect->Width) == 0)
2023     {
2024         rel_width = 1.0;
2025         nwidth = INT_MAX;
2026     }
2027     else
2028     {
2029         rel_width = sqrt((corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) +
2030                          (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y))
2031                          / rect->Width;
2032         nwidth = roundr(rel_width * rect->Width);
2033     }
2034
2035     if (roundr(rect->Height) == 0)
2036     {
2037         rel_height = 1.0;
2038         nheight = INT_MAX;
2039     }
2040     else
2041     {
2042         rel_height = sqrt((corners[2].x - corners[1].x) * (corners[2].x - corners[1].x) +
2043                           (corners[2].y - corners[1].y) * (corners[2].y - corners[1].y))
2044                           / rect->Height;
2045         nheight = roundr(rel_height * rect->Height);
2046     }
2047
2048     if (roundr(rect->Width) != 0 && roundr(rect->Height) != 0)
2049     {
2050         /* FIXME: If only the width or only the height is 0, we should probably still clip */
2051         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
2052         SelectClipRgn(graphics->hdc, rgn);
2053     }
2054
2055     /* Use gdi to find the font, then perform transformations on it (height,
2056      * width, angle). */
2057     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2058     GetTextMetricsW(graphics->hdc, &textmet);
2059     lfw = font->lfw;
2060
2061     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
2062     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
2063
2064     pt[0].X = 0.0;
2065     pt[0].Y = 0.0;
2066     pt[1].X = 1.0;
2067     pt[1].Y = 0.0;
2068     GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
2069     angle = gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2070     ang_cos = cos(angle);
2071     ang_sin = sin(angle);
2072     lfw.lfEscapement = lfw.lfOrientation = -roundr((angle / M_PI) * 1800.0);
2073
2074     gdifont = CreateFontIndirectW(&lfw);
2075     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
2076
2077     for(i = 0, j = 0; i < length; i++){
2078         if(!isprintW(string[i]) && (string[i] != '\n'))
2079             continue;
2080
2081         stringdup[j] = string[i];
2082         j++;
2083     }
2084
2085     length = j;
2086
2087     while(sum < length){
2088         drawcoord.left = corners[0].x + roundr(ang_sin * (REAL) height);
2089         drawcoord.top = corners[0].y + roundr(ang_cos * (REAL) height);
2090
2091         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2092                               nwidth, &fit, NULL, &size);
2093         fitcpy = fit;
2094
2095         if(fit == 0){
2096             DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, DT_NOCLIP |
2097                       DT_EXPANDTABS);
2098             break;
2099         }
2100
2101         for(lret = 0; lret < fit; lret++)
2102             if(*(stringdup + sum + lret) == '\n')
2103                 break;
2104
2105         /* Line break code (may look strange, but it imitates windows). */
2106         if(lret < fit)
2107             fit = lret;    /* this is not an off-by-one error */
2108         else if(fit < (length - sum)){
2109             if(*(stringdup + sum + fit) == ' ')
2110                 while(*(stringdup + sum + fit) == ' ')
2111                     fit++;
2112             else
2113                 while(*(stringdup + sum + fit - 1) != ' '){
2114                     fit--;
2115
2116                     if(*(stringdup + sum + fit) == '\t')
2117                         break;
2118
2119                     if(fit == 0){
2120                         fit = fitcpy;
2121                         break;
2122                     }
2123                 }
2124         }
2125         DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
2126                   &drawcoord, DT_NOCLIP | DT_EXPANDTABS);
2127
2128         sum += fit + (lret < fitcpy ? 1 : 0);
2129         height += size.cy;
2130
2131         if(height > nheight)
2132             break;
2133
2134         /* Stop if this was a linewrap (but not if it was a linebreak). */
2135         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2136             break;
2137     }
2138
2139     GdipFree(stringdup);
2140     DeleteObject(rgn);
2141     DeleteObject(gdifont);
2142
2143     RestoreDC(graphics->hdc, save_state);
2144
2145     return Ok;
2146 }
2147
2148 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2149     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2150 {
2151     GpPath *path;
2152     GpStatus stat;
2153
2154     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2155             count, tension, fill);
2156
2157     if(!graphics || !brush || !points)
2158         return InvalidParameter;
2159
2160     if(graphics->busy)
2161         return ObjectBusy;
2162
2163     stat = GdipCreatePath(fill, &path);
2164     if(stat != Ok)
2165         return stat;
2166
2167     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2168     if(stat != Ok){
2169         GdipDeletePath(path);
2170         return stat;
2171     }
2172
2173     stat = GdipFillPath(graphics, brush, path);
2174     if(stat != Ok){
2175         GdipDeletePath(path);
2176         return stat;
2177     }
2178
2179     GdipDeletePath(path);
2180
2181     return Ok;
2182 }
2183
2184 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2185     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2186 {
2187     GpPointF *ptf;
2188     GpStatus stat;
2189     INT i;
2190
2191     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2192             count, tension, fill);
2193
2194     if(!points || count <= 0)
2195         return InvalidParameter;
2196
2197     ptf = GdipAlloc(sizeof(GpPointF)*count);
2198     if(!ptf)
2199         return OutOfMemory;
2200
2201     for(i = 0;i < count;i++){
2202         ptf[i].X = (REAL)points[i].X;
2203         ptf[i].Y = (REAL)points[i].Y;
2204     }
2205
2206     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2207
2208     GdipFree(ptf);
2209
2210     return stat;
2211 }
2212
2213 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2214     REAL y, REAL width, REAL height)
2215 {
2216     INT save_state;
2217     GpPointF ptf[2];
2218     POINT pti[2];
2219
2220     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2221
2222     if(!graphics || !brush)
2223         return InvalidParameter;
2224
2225     if(graphics->busy)
2226         return ObjectBusy;
2227
2228     ptf[0].X = x;
2229     ptf[0].Y = y;
2230     ptf[1].X = x + width;
2231     ptf[1].Y = y + height;
2232
2233     save_state = SaveDC(graphics->hdc);
2234     EndPath(graphics->hdc);
2235     SelectObject(graphics->hdc, brush->gdibrush);
2236     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2237
2238     transform_and_round_points(graphics, pti, ptf, 2);
2239
2240     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2241
2242     RestoreDC(graphics->hdc, save_state);
2243
2244     return Ok;
2245 }
2246
2247 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2248     INT y, INT width, INT height)
2249 {
2250     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2251
2252     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2253 }
2254
2255 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2256 {
2257     INT save_state;
2258     GpStatus retval;
2259
2260     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2261
2262     if(!brush || !graphics || !path)
2263         return InvalidParameter;
2264
2265     if(graphics->busy)
2266         return ObjectBusy;
2267
2268     save_state = SaveDC(graphics->hdc);
2269     EndPath(graphics->hdc);
2270     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2271                                                                     : WINDING));
2272
2273     BeginPath(graphics->hdc);
2274     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2275                        path->pathdata.Types, path->pathdata.Count, FALSE);
2276
2277     if(retval != Ok)
2278         goto end;
2279
2280     EndPath(graphics->hdc);
2281     brush_fill_path(graphics, brush);
2282
2283     retval = Ok;
2284
2285 end:
2286     RestoreDC(graphics->hdc, save_state);
2287
2288     return retval;
2289 }
2290
2291 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2292     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2293 {
2294     INT save_state;
2295
2296     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2297             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2298
2299     if(!graphics || !brush)
2300         return InvalidParameter;
2301
2302     if(graphics->busy)
2303         return ObjectBusy;
2304
2305     save_state = SaveDC(graphics->hdc);
2306     EndPath(graphics->hdc);
2307     SelectObject(graphics->hdc, brush->gdibrush);
2308     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2309
2310     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2311
2312     RestoreDC(graphics->hdc, save_state);
2313
2314     return Ok;
2315 }
2316
2317 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2318     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2319 {
2320     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2321             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2322
2323     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2324 }
2325
2326 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2327     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2328 {
2329     INT save_state;
2330     GpPointF *ptf = NULL;
2331     POINT *pti = NULL;
2332     GpStatus retval = Ok;
2333
2334     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2335
2336     if(!graphics || !brush || !points || !count)
2337         return InvalidParameter;
2338
2339     if(graphics->busy)
2340         return ObjectBusy;
2341
2342     ptf = GdipAlloc(count * sizeof(GpPointF));
2343     pti = GdipAlloc(count * sizeof(POINT));
2344     if(!ptf || !pti){
2345         retval = OutOfMemory;
2346         goto end;
2347     }
2348
2349     memcpy(ptf, points, count * sizeof(GpPointF));
2350
2351     save_state = SaveDC(graphics->hdc);
2352     EndPath(graphics->hdc);
2353     SelectObject(graphics->hdc, brush->gdibrush);
2354     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2355     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2356                                                                   : WINDING));
2357
2358     transform_and_round_points(graphics, pti, ptf, count);
2359     Polygon(graphics->hdc, pti, count);
2360
2361     RestoreDC(graphics->hdc, save_state);
2362
2363 end:
2364     GdipFree(ptf);
2365     GdipFree(pti);
2366
2367     return retval;
2368 }
2369
2370 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2371     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2372 {
2373     INT save_state, i;
2374     GpPointF *ptf = NULL;
2375     POINT *pti = NULL;
2376     GpStatus retval = Ok;
2377
2378     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2379
2380     if(!graphics || !brush || !points || !count)
2381         return InvalidParameter;
2382
2383     if(graphics->busy)
2384         return ObjectBusy;
2385
2386     ptf = GdipAlloc(count * sizeof(GpPointF));
2387     pti = GdipAlloc(count * sizeof(POINT));
2388     if(!ptf || !pti){
2389         retval = OutOfMemory;
2390         goto end;
2391     }
2392
2393     for(i = 0; i < count; i ++){
2394         ptf[i].X = (REAL) points[i].X;
2395         ptf[i].Y = (REAL) points[i].Y;
2396     }
2397
2398     save_state = SaveDC(graphics->hdc);
2399     EndPath(graphics->hdc);
2400     SelectObject(graphics->hdc, brush->gdibrush);
2401     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2402     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2403                                                                   : WINDING));
2404
2405     transform_and_round_points(graphics, pti, ptf, count);
2406     Polygon(graphics->hdc, pti, count);
2407
2408     RestoreDC(graphics->hdc, save_state);
2409
2410 end:
2411     GdipFree(ptf);
2412     GdipFree(pti);
2413
2414     return retval;
2415 }
2416
2417 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2418     GDIPCONST GpPointF *points, INT count)
2419 {
2420     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2421
2422     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2423 }
2424
2425 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2426     GDIPCONST GpPoint *points, INT count)
2427 {
2428     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2429
2430     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2431 }
2432
2433 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2434     REAL x, REAL y, REAL width, REAL height)
2435 {
2436     INT save_state;
2437     GpPointF ptf[4];
2438     POINT pti[4];
2439
2440     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2441
2442     if(!graphics || !brush)
2443         return InvalidParameter;
2444
2445     if(graphics->busy)
2446         return ObjectBusy;
2447
2448     ptf[0].X = x;
2449     ptf[0].Y = y;
2450     ptf[1].X = x + width;
2451     ptf[1].Y = y;
2452     ptf[2].X = x + width;
2453     ptf[2].Y = y + height;
2454     ptf[3].X = x;
2455     ptf[3].Y = y + height;
2456
2457     save_state = SaveDC(graphics->hdc);
2458     EndPath(graphics->hdc);
2459     SelectObject(graphics->hdc, brush->gdibrush);
2460     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2461
2462     transform_and_round_points(graphics, pti, ptf, 4);
2463
2464     Polygon(graphics->hdc, pti, 4);
2465
2466     RestoreDC(graphics->hdc, save_state);
2467
2468     return Ok;
2469 }
2470
2471 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2472     INT x, INT y, INT width, INT height)
2473 {
2474     INT save_state;
2475     GpPointF ptf[4];
2476     POINT pti[4];
2477
2478     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2479
2480     if(!graphics || !brush)
2481         return InvalidParameter;
2482
2483     if(graphics->busy)
2484         return ObjectBusy;
2485
2486     ptf[0].X = x;
2487     ptf[0].Y = y;
2488     ptf[1].X = x + width;
2489     ptf[1].Y = y;
2490     ptf[2].X = x + width;
2491     ptf[2].Y = y + height;
2492     ptf[3].X = x;
2493     ptf[3].Y = y + height;
2494
2495     save_state = SaveDC(graphics->hdc);
2496     EndPath(graphics->hdc);
2497     SelectObject(graphics->hdc, brush->gdibrush);
2498     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2499
2500     transform_and_round_points(graphics, pti, ptf, 4);
2501
2502     Polygon(graphics->hdc, pti, 4);
2503
2504     RestoreDC(graphics->hdc, save_state);
2505
2506     return Ok;
2507 }
2508
2509 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2510     INT count)
2511 {
2512     GpStatus ret;
2513     INT i;
2514
2515     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2516
2517     if(!rects)
2518         return InvalidParameter;
2519
2520     for(i = 0; i < count; i++){
2521         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2522         if(ret != Ok)   return ret;
2523     }
2524
2525     return Ok;
2526 }
2527
2528 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2529     INT count)
2530 {
2531     GpRectF *rectsF;
2532     GpStatus ret;
2533     INT i;
2534
2535     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2536
2537     if(!rects || count <= 0)
2538         return InvalidParameter;
2539
2540     rectsF = GdipAlloc(sizeof(GpRectF)*count);
2541     if(!rectsF)
2542         return OutOfMemory;
2543
2544     for(i = 0; i < count; i++){
2545         rectsF[i].X      = (REAL)rects[i].X;
2546         rectsF[i].Y      = (REAL)rects[i].Y;
2547         rectsF[i].X      = (REAL)rects[i].Width;
2548         rectsF[i].Height = (REAL)rects[i].Height;
2549     }
2550
2551     ret = GdipFillRectangles(graphics,brush,rectsF,count);
2552     GdipFree(rectsF);
2553
2554     return ret;
2555 }
2556
2557 /*****************************************************************************
2558  * GdipFillRegion [GDIPLUS.@]
2559  */
2560 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2561         GpRegion* region)
2562 {
2563     INT save_state;
2564     GpStatus status;
2565     HRGN hrgn;
2566
2567     TRACE("(%p, %p, %p)\n", graphics, brush, region);
2568
2569     if (!(graphics && brush && region))
2570         return InvalidParameter;
2571
2572     if(graphics->busy)
2573         return ObjectBusy;
2574
2575     status = GdipGetRegionHRgn(region, graphics, &hrgn);
2576     if(status != Ok)
2577         return status;
2578
2579     save_state = SaveDC(graphics->hdc);
2580     EndPath(graphics->hdc);
2581     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2582
2583     FillRgn(graphics->hdc, hrgn, brush->gdibrush);
2584
2585     RestoreDC(graphics->hdc, save_state);
2586
2587     DeleteObject(hrgn);
2588
2589     return Ok;
2590 }
2591
2592 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2593 {
2594     static int calls;
2595
2596     if(!graphics)
2597         return InvalidParameter;
2598
2599     if(graphics->busy)
2600         return ObjectBusy;
2601
2602     if(!(calls++))
2603         FIXME("not implemented\n");
2604
2605     return NotImplemented;
2606 }
2607
2608 /*****************************************************************************
2609  * GdipGetClipBounds [GDIPLUS.@]
2610  */
2611 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
2612 {
2613     TRACE("(%p, %p)\n", graphics, rect);
2614
2615     if(!graphics)
2616         return InvalidParameter;
2617
2618     if(graphics->busy)
2619         return ObjectBusy;
2620
2621     return GdipGetRegionBounds(graphics->clip, graphics, rect);
2622 }
2623
2624 /*****************************************************************************
2625  * GdipGetClipBoundsI [GDIPLUS.@]
2626  */
2627 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
2628 {
2629     TRACE("(%p, %p)\n", graphics, rect);
2630
2631     if(!graphics)
2632         return InvalidParameter;
2633
2634     if(graphics->busy)
2635         return ObjectBusy;
2636
2637     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
2638 }
2639
2640 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2641 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2642     CompositingMode *mode)
2643 {
2644     TRACE("(%p, %p)\n", graphics, mode);
2645
2646     if(!graphics || !mode)
2647         return InvalidParameter;
2648
2649     if(graphics->busy)
2650         return ObjectBusy;
2651
2652     *mode = graphics->compmode;
2653
2654     return Ok;
2655 }
2656
2657 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2658 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2659     CompositingQuality *quality)
2660 {
2661     TRACE("(%p, %p)\n", graphics, quality);
2662
2663     if(!graphics || !quality)
2664         return InvalidParameter;
2665
2666     if(graphics->busy)
2667         return ObjectBusy;
2668
2669     *quality = graphics->compqual;
2670
2671     return Ok;
2672 }
2673
2674 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2675 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2676     InterpolationMode *mode)
2677 {
2678     TRACE("(%p, %p)\n", graphics, mode);
2679
2680     if(!graphics || !mode)
2681         return InvalidParameter;
2682
2683     if(graphics->busy)
2684         return ObjectBusy;
2685
2686     *mode = graphics->interpolation;
2687
2688     return Ok;
2689 }
2690
2691 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2692 {
2693     if(!graphics || !argb)
2694         return InvalidParameter;
2695
2696     if(graphics->busy)
2697         return ObjectBusy;
2698
2699     FIXME("(%p, %p): stub\n", graphics, argb);
2700
2701     return NotImplemented;
2702 }
2703
2704 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2705 {
2706     TRACE("(%p, %p)\n", graphics, scale);
2707
2708     if(!graphics || !scale)
2709         return InvalidParameter;
2710
2711     if(graphics->busy)
2712         return ObjectBusy;
2713
2714     *scale = graphics->scale;
2715
2716     return Ok;
2717 }
2718
2719 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2720 {
2721     TRACE("(%p, %p)\n", graphics, unit);
2722
2723     if(!graphics || !unit)
2724         return InvalidParameter;
2725
2726     if(graphics->busy)
2727         return ObjectBusy;
2728
2729     *unit = graphics->unit;
2730
2731     return Ok;
2732 }
2733
2734 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2735 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2736     *mode)
2737 {
2738     TRACE("(%p, %p)\n", graphics, mode);
2739
2740     if(!graphics || !mode)
2741         return InvalidParameter;
2742
2743     if(graphics->busy)
2744         return ObjectBusy;
2745
2746     *mode = graphics->pixeloffset;
2747
2748     return Ok;
2749 }
2750
2751 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2752 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2753 {
2754     TRACE("(%p, %p)\n", graphics, mode);
2755
2756     if(!graphics || !mode)
2757         return InvalidParameter;
2758
2759     if(graphics->busy)
2760         return ObjectBusy;
2761
2762     *mode = graphics->smoothing;
2763
2764     return Ok;
2765 }
2766
2767 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2768 {
2769     TRACE("(%p, %p)\n", graphics, contrast);
2770
2771     if(!graphics || !contrast)
2772         return InvalidParameter;
2773
2774     *contrast = graphics->textcontrast;
2775
2776     return Ok;
2777 }
2778
2779 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2780 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2781     TextRenderingHint *hint)
2782 {
2783     TRACE("(%p, %p)\n", graphics, hint);
2784
2785     if(!graphics || !hint)
2786         return InvalidParameter;
2787
2788     if(graphics->busy)
2789         return ObjectBusy;
2790
2791     *hint = graphics->texthint;
2792
2793     return Ok;
2794 }
2795
2796 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2797 {
2798     TRACE("(%p, %p)\n", graphics, matrix);
2799
2800     if(!graphics || !matrix)
2801         return InvalidParameter;
2802
2803     if(graphics->busy)
2804         return ObjectBusy;
2805
2806     *matrix = *graphics->worldtrans;
2807     return Ok;
2808 }
2809
2810 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2811 {
2812     GpSolidFill *brush;
2813     GpStatus stat;
2814     RECT rect;
2815
2816     TRACE("(%p, %x)\n", graphics, color);
2817
2818     if(!graphics)
2819         return InvalidParameter;
2820
2821     if(graphics->busy)
2822         return ObjectBusy;
2823
2824     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2825         return stat;
2826
2827     if(graphics->hwnd){
2828         if(!GetWindowRect(graphics->hwnd, &rect)){
2829             GdipDeleteBrush((GpBrush*)brush);
2830             return GenericError;
2831         }
2832
2833         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right  - rect.left),
2834                                                                (REAL)(rect.bottom - rect.top));
2835     }
2836     else
2837         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2838                                                                (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2839
2840     GdipDeleteBrush((GpBrush*)brush);
2841
2842     return Ok;
2843 }
2844
2845 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2846 {
2847     TRACE("(%p, %p)\n", graphics, res);
2848
2849     if(!graphics || !res)
2850         return InvalidParameter;
2851
2852     return GdipIsEmptyRegion(graphics->clip, graphics, res);
2853 }
2854
2855 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2856 {
2857     FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2858
2859     if(!graphics || !result)
2860         return InvalidParameter;
2861
2862     if(graphics->busy)
2863         return ObjectBusy;
2864
2865     return NotImplemented;
2866 }
2867
2868 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2869 {
2870     FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2871
2872     if(!graphics || !result)
2873         return InvalidParameter;
2874
2875     if(graphics->busy)
2876         return ObjectBusy;
2877
2878     return NotImplemented;
2879 }
2880
2881 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2882         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2883         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2884         INT regionCount, GpRegion** regions)
2885 {
2886     if (!(graphics && string && font && layoutRect && stringFormat && regions))
2887         return InvalidParameter;
2888
2889     FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2890             length, font, layoutRect, stringFormat, regionCount, regions);
2891
2892     return NotImplemented;
2893 }
2894
2895 /* Find the smallest rectangle that bounds the text when it is printed in rect
2896  * according to the format options listed in format. If rect has 0 width and
2897  * height, then just find the smallest rectangle that bounds the text when it's
2898  * printed at location (rect->X, rect-Y). */
2899 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2900     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2901     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2902     INT *codepointsfitted, INT *linesfilled)
2903 {
2904     HFONT oldfont;
2905     WCHAR* stringdup;
2906     INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2907         nheight;
2908     SIZE size;
2909
2910     if(!graphics || !string || !font || !rect)
2911         return InvalidParameter;
2912
2913     if(linesfilled) *linesfilled = 0;
2914     if(codepointsfitted) *codepointsfitted = 0;
2915
2916     if(format)
2917         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2918
2919     if(length == -1) length = lstrlenW(string);
2920
2921     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2922     if(!stringdup) return OutOfMemory;
2923
2924     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2925     nwidth = roundr(rect->Width);
2926     nheight = roundr(rect->Height);
2927
2928     if((nwidth == 0) && (nheight == 0))
2929         nwidth = nheight = INT_MAX;
2930
2931     for(i = 0, j = 0; i < length; i++){
2932         if(!isprintW(string[i]) && (string[i] != '\n'))
2933             continue;
2934
2935         stringdup[j] = string[i];
2936         j++;
2937     }
2938
2939     stringdup[j] = 0;
2940     length = j;
2941
2942     while(sum < length){
2943         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2944                               nwidth, &fit, NULL, &size);
2945         fitcpy = fit;
2946
2947         if(fit == 0)
2948             break;
2949
2950         for(lret = 0; lret < fit; lret++)
2951             if(*(stringdup + sum + lret) == '\n')
2952                 break;
2953
2954         /* Line break code (may look strange, but it imitates windows). */
2955         if(lret < fit)
2956             fit = lret;    /* this is not an off-by-one error */
2957         else if(fit < (length - sum)){
2958             if(*(stringdup + sum + fit) == ' ')
2959                 while(*(stringdup + sum + fit) == ' ')
2960                     fit++;
2961             else
2962                 while(*(stringdup + sum + fit - 1) != ' '){
2963                     fit--;
2964
2965                     if(*(stringdup + sum + fit) == '\t')
2966                         break;
2967
2968                     if(fit == 0){
2969                         fit = fitcpy;
2970                         break;
2971                     }
2972                 }
2973         }
2974
2975         GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2976                               nwidth, &j, NULL, &size);
2977
2978         sum += fit + (lret < fitcpy ? 1 : 0);
2979         if(codepointsfitted) *codepointsfitted = sum;
2980
2981         height += size.cy;
2982         if(linesfilled) *linesfilled += size.cy;
2983         max_width = max(max_width, size.cx);
2984
2985         if(height > nheight)
2986             break;
2987
2988         /* Stop if this was a linewrap (but not if it was a linebreak). */
2989         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2990             break;
2991     }
2992
2993     bounds->X = rect->X;
2994     bounds->Y = rect->Y;
2995     bounds->Width = (REAL)max_width;
2996     bounds->Height = (REAL) min(height, nheight);
2997
2998     GdipFree(stringdup);
2999     DeleteObject(SelectObject(graphics->hdc, oldfont));
3000
3001     return Ok;
3002 }
3003
3004 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3005 {
3006     TRACE("(%p)\n", graphics);
3007
3008     if(!graphics)
3009         return InvalidParameter;
3010
3011     if(graphics->busy)
3012         return ObjectBusy;
3013
3014     return GdipSetInfinite(graphics->clip);
3015 }
3016
3017 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3018 {
3019     TRACE("(%p)\n", graphics);
3020
3021     if(!graphics)
3022         return InvalidParameter;
3023
3024     if(graphics->busy)
3025         return ObjectBusy;
3026
3027     graphics->worldtrans->matrix[0] = 1.0;
3028     graphics->worldtrans->matrix[1] = 0.0;
3029     graphics->worldtrans->matrix[2] = 0.0;
3030     graphics->worldtrans->matrix[3] = 1.0;
3031     graphics->worldtrans->matrix[4] = 0.0;
3032     graphics->worldtrans->matrix[5] = 0.0;
3033
3034     return Ok;
3035 }
3036
3037 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3038 {
3039     static int calls;
3040
3041     if(!graphics)
3042         return InvalidParameter;
3043
3044     if(!(calls++))
3045         FIXME("graphics state not implemented\n");
3046
3047     return Ok;
3048 }
3049
3050 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3051     GpMatrixOrder order)
3052 {
3053     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3054
3055     if(!graphics)
3056         return InvalidParameter;
3057
3058     if(graphics->busy)
3059         return ObjectBusy;
3060
3061     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3062 }
3063
3064 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3065 {
3066     static int calls;
3067
3068     if(!graphics || !state)
3069         return InvalidParameter;
3070
3071     if(!(calls++))
3072         FIXME("graphics state not implemented\n");
3073
3074     *state = 0xdeadbeef;
3075     return Ok;
3076 }
3077
3078 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
3079 {
3080     FIXME("(%p, %p)\n", graphics, state);
3081
3082     if(!graphics || !state)
3083         return InvalidParameter;
3084
3085     *state = 0xdeadbeef;
3086     return Ok;
3087 }
3088
3089 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
3090 {
3091     FIXME("(%p, 0x%x)\n", graphics, state);
3092
3093     if(!graphics || !state)
3094         return InvalidParameter;
3095
3096     return Ok;
3097 }
3098
3099 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
3100     REAL sy, GpMatrixOrder order)
3101 {
3102     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
3103
3104     if(!graphics)
3105         return InvalidParameter;
3106
3107     if(graphics->busy)
3108         return ObjectBusy;
3109
3110     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
3111 }
3112
3113 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
3114     CombineMode mode)
3115 {
3116     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
3117
3118     if(!graphics || !srcgraphics)
3119         return InvalidParameter;
3120
3121     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
3122 }
3123
3124 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
3125     CompositingMode mode)
3126 {
3127     TRACE("(%p, %d)\n", graphics, mode);
3128
3129     if(!graphics)
3130         return InvalidParameter;
3131
3132     if(graphics->busy)
3133         return ObjectBusy;
3134
3135     graphics->compmode = mode;
3136
3137     return Ok;
3138 }
3139
3140 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
3141     CompositingQuality quality)
3142 {
3143     TRACE("(%p, %d)\n", graphics, quality);
3144
3145     if(!graphics)
3146         return InvalidParameter;
3147
3148     if(graphics->busy)
3149         return ObjectBusy;
3150
3151     graphics->compqual = quality;
3152
3153     return Ok;
3154 }
3155
3156 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
3157     InterpolationMode mode)
3158 {
3159     TRACE("(%p, %d)\n", graphics, mode);
3160
3161     if(!graphics)
3162         return InvalidParameter;
3163
3164     if(graphics->busy)
3165         return ObjectBusy;
3166
3167     graphics->interpolation = mode;
3168
3169     return Ok;
3170 }
3171
3172 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
3173 {
3174     TRACE("(%p, %.2f)\n", graphics, scale);
3175
3176     if(!graphics || (scale <= 0.0))
3177         return InvalidParameter;
3178
3179     if(graphics->busy)
3180         return ObjectBusy;
3181
3182     graphics->scale = scale;
3183
3184     return Ok;
3185 }
3186
3187 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
3188 {
3189     TRACE("(%p, %d)\n", graphics, unit);
3190
3191     if(!graphics)
3192         return InvalidParameter;
3193
3194     if(graphics->busy)
3195         return ObjectBusy;
3196
3197     if(unit == UnitWorld)
3198         return InvalidParameter;
3199
3200     graphics->unit = unit;
3201
3202     return Ok;
3203 }
3204
3205 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3206     mode)
3207 {
3208     TRACE("(%p, %d)\n", graphics, mode);
3209
3210     if(!graphics)
3211         return InvalidParameter;
3212
3213     if(graphics->busy)
3214         return ObjectBusy;
3215
3216     graphics->pixeloffset = mode;
3217
3218     return Ok;
3219 }
3220
3221 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
3222 {
3223     TRACE("(%p, %d)\n", graphics, mode);
3224
3225     if(!graphics)
3226         return InvalidParameter;
3227
3228     if(graphics->busy)
3229         return ObjectBusy;
3230
3231     graphics->smoothing = mode;
3232
3233     return Ok;
3234 }
3235
3236 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3237 {
3238     TRACE("(%p, %d)\n", graphics, contrast);
3239
3240     if(!graphics)
3241         return InvalidParameter;
3242
3243     graphics->textcontrast = contrast;
3244
3245     return Ok;
3246 }
3247
3248 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3249     TextRenderingHint hint)
3250 {
3251     TRACE("(%p, %d)\n", graphics, hint);
3252
3253     if(!graphics)
3254         return InvalidParameter;
3255
3256     if(graphics->busy)
3257         return ObjectBusy;
3258
3259     graphics->texthint = hint;
3260
3261     return Ok;
3262 }
3263
3264 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3265 {
3266     TRACE("(%p, %p)\n", graphics, matrix);
3267
3268     if(!graphics || !matrix)
3269         return InvalidParameter;
3270
3271     if(graphics->busy)
3272         return ObjectBusy;
3273
3274     GdipDeleteMatrix(graphics->worldtrans);
3275     return GdipCloneMatrix(matrix, &graphics->worldtrans);
3276 }
3277
3278 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3279     REAL dy, GpMatrixOrder order)
3280 {
3281     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3282
3283     if(!graphics)
3284         return InvalidParameter;
3285
3286     if(graphics->busy)
3287         return ObjectBusy;
3288
3289     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3290 }
3291
3292 /*****************************************************************************
3293  * GdipSetClipHrgn [GDIPLUS.@]
3294  */
3295 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
3296 {
3297     GpRegion *region;
3298     GpStatus status;
3299
3300     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
3301
3302     if(!graphics)
3303         return InvalidParameter;
3304
3305     status = GdipCreateRegionHrgn(hrgn, &region);
3306     if(status != Ok)
3307         return status;
3308
3309     status = GdipSetClipRegion(graphics, region, mode);
3310
3311     GdipDeleteRegion(region);
3312     return status;
3313 }
3314
3315 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3316 {
3317     TRACE("(%p, %p, %d)\n", graphics, path, mode);
3318
3319     if(!graphics)
3320         return InvalidParameter;
3321
3322     if(graphics->busy)
3323         return ObjectBusy;
3324
3325     return GdipCombineRegionPath(graphics->clip, path, mode);
3326 }
3327
3328 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3329                                     REAL width, REAL height,
3330                                     CombineMode mode)
3331 {
3332     GpRectF rect;
3333
3334     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3335
3336     if(!graphics)
3337         return InvalidParameter;
3338
3339     if(graphics->busy)
3340         return ObjectBusy;
3341
3342     rect.X = x;
3343     rect.Y = y;
3344     rect.Width  = width;
3345     rect.Height = height;
3346
3347     return GdipCombineRegionRect(graphics->clip, &rect, mode);
3348 }
3349
3350 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3351                                      INT width, INT height,
3352                                      CombineMode mode)
3353 {
3354     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3355
3356     if(!graphics)
3357         return InvalidParameter;
3358
3359     if(graphics->busy)
3360         return ObjectBusy;
3361
3362     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3363 }
3364
3365 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3366                                       CombineMode mode)
3367 {
3368     TRACE("(%p, %p, %d)\n", graphics, region, mode);
3369
3370     if(!graphics || !region)
3371         return InvalidParameter;
3372
3373     if(graphics->busy)
3374         return ObjectBusy;
3375
3376     return GdipCombineRegionRegion(graphics->clip, region, mode);
3377 }
3378
3379 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3380     UINT limitDpi)
3381 {
3382     static int calls;
3383
3384     if(!(calls++))
3385         FIXME("not implemented\n");
3386
3387     return NotImplemented;
3388 }
3389
3390 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3391     INT count)
3392 {
3393     INT save_state;
3394     POINT *pti;
3395
3396     TRACE("(%p, %p, %d)\n", graphics, points, count);
3397
3398     if(!graphics || !pen || count<=0)
3399         return InvalidParameter;
3400
3401     if(graphics->busy)
3402         return ObjectBusy;
3403
3404     pti = GdipAlloc(sizeof(POINT) * count);
3405
3406     save_state = prepare_dc(graphics, pen);
3407     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3408
3409     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3410     Polygon(graphics->hdc, pti, count);
3411
3412     restore_dc(graphics, save_state);
3413     GdipFree(pti);
3414
3415     return Ok;
3416 }
3417
3418 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3419     INT count)
3420 {
3421     GpStatus ret;
3422     GpPointF *ptf;
3423     INT i;
3424
3425     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3426
3427     if(count<=0)    return InvalidParameter;
3428     ptf = GdipAlloc(sizeof(GpPointF) * count);
3429
3430     for(i = 0;i < count; i++){
3431         ptf[i].X = (REAL)points[i].X;
3432         ptf[i].Y = (REAL)points[i].Y;
3433     }
3434
3435     ret = GdipDrawPolygon(graphics,pen,ptf,count);
3436     GdipFree(ptf);
3437
3438     return ret;
3439 }
3440
3441 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3442 {
3443     TRACE("(%p, %p)\n", graphics, dpi);
3444
3445     if(!graphics || !dpi)
3446         return InvalidParameter;
3447
3448     if(graphics->busy)
3449         return ObjectBusy;
3450
3451     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3452
3453     return Ok;
3454 }
3455
3456 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3457 {
3458     TRACE("(%p, %p)\n", graphics, dpi);
3459
3460     if(!graphics || !dpi)
3461         return InvalidParameter;
3462
3463     if(graphics->busy)
3464         return ObjectBusy;
3465
3466     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3467
3468     return Ok;
3469 }
3470
3471 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3472     GpMatrixOrder order)
3473 {
3474     GpMatrix m;
3475     GpStatus ret;
3476
3477     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3478
3479     if(!graphics || !matrix)
3480         return InvalidParameter;
3481
3482     if(graphics->busy)
3483         return ObjectBusy;
3484
3485     m = *(graphics->worldtrans);
3486
3487     ret = GdipMultiplyMatrix(&m, matrix, order);
3488     if(ret == Ok)
3489         *(graphics->worldtrans) = m;
3490
3491     return ret;
3492 }
3493
3494 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3495 {
3496     TRACE("(%p, %p)\n", graphics, hdc);
3497
3498     if(!graphics || !hdc)
3499         return InvalidParameter;
3500
3501     if(graphics->busy)
3502         return ObjectBusy;
3503
3504     *hdc = graphics->hdc;
3505     graphics->busy = TRUE;
3506
3507     return Ok;
3508 }
3509
3510 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3511 {
3512     TRACE("(%p, %p)\n", graphics, hdc);
3513
3514     if(!graphics)
3515         return InvalidParameter;
3516
3517     if(graphics->hdc != hdc || !(graphics->busy))
3518         return InvalidParameter;
3519
3520     graphics->busy = FALSE;
3521
3522     return Ok;
3523 }
3524
3525 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3526 {
3527     GpRegion *clip;
3528     GpStatus status;
3529
3530     TRACE("(%p, %p)\n", graphics, region);
3531
3532     if(!graphics || !region)
3533         return InvalidParameter;
3534
3535     if(graphics->busy)
3536         return ObjectBusy;
3537
3538     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3539         return status;
3540
3541     /* free everything except root node and header */
3542     delete_element(&region->node);
3543     memcpy(region, clip, sizeof(GpRegion));
3544
3545     return Ok;
3546 }
3547
3548 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3549                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
3550 {
3551     if(!graphics || !points || count <= 0)
3552         return InvalidParameter;
3553
3554     if(graphics->busy)
3555         return ObjectBusy;
3556
3557     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3558
3559     return NotImplemented;
3560 }
3561
3562 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3563                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
3564 {
3565     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3566
3567     return NotImplemented;
3568 }
3569
3570 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3571 {
3572     FIXME("\n");
3573
3574     return NULL;
3575 }
3576
3577 /*****************************************************************************
3578  * GdipTranslateClip [GDIPLUS.@]
3579  */
3580 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
3581 {
3582     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
3583
3584     if(!graphics)
3585         return InvalidParameter;
3586
3587     if(graphics->busy)
3588         return ObjectBusy;
3589
3590     return GdipTranslateRegion(graphics->clip, dx, dy);
3591 }
3592
3593 /*****************************************************************************
3594  * GdipTranslateClipI [GDIPLUS.@]
3595  */
3596 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
3597 {
3598     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
3599
3600     if(!graphics)
3601         return InvalidParameter;
3602
3603     if(graphics->busy)
3604         return ObjectBusy;
3605
3606     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
3607 }