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