wined3d: Pack hardcoded local constants in ARB.
[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     SelectObject(graphics->hdc, brush->gdibrush);
2483     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2484
2485     transform_and_round_points(graphics, pti, ptf, 4);
2486
2487     Polygon(graphics->hdc, pti, 4);
2488
2489     RestoreDC(graphics->hdc, save_state);
2490
2491     return Ok;
2492 }
2493
2494 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2495     INT x, INT y, INT width, INT height)
2496 {
2497     INT save_state;
2498     GpPointF ptf[4];
2499     POINT pti[4];
2500
2501     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2502
2503     if(!graphics || !brush)
2504         return InvalidParameter;
2505
2506     if(graphics->busy)
2507         return ObjectBusy;
2508
2509     ptf[0].X = x;
2510     ptf[0].Y = y;
2511     ptf[1].X = x + width;
2512     ptf[1].Y = y;
2513     ptf[2].X = x + width;
2514     ptf[2].Y = y + height;
2515     ptf[3].X = x;
2516     ptf[3].Y = y + height;
2517
2518     save_state = SaveDC(graphics->hdc);
2519     EndPath(graphics->hdc);
2520     SelectObject(graphics->hdc, brush->gdibrush);
2521     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2522
2523     transform_and_round_points(graphics, pti, ptf, 4);
2524
2525     Polygon(graphics->hdc, pti, 4);
2526
2527     RestoreDC(graphics->hdc, save_state);
2528
2529     return Ok;
2530 }
2531
2532 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2533     INT count)
2534 {
2535     GpStatus ret;
2536     INT i;
2537
2538     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2539
2540     if(!rects)
2541         return InvalidParameter;
2542
2543     for(i = 0; i < count; i++){
2544         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2545         if(ret != Ok)   return ret;
2546     }
2547
2548     return Ok;
2549 }
2550
2551 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2552     INT count)
2553 {
2554     GpRectF *rectsF;
2555     GpStatus ret;
2556     INT i;
2557
2558     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2559
2560     if(!rects || count <= 0)
2561         return InvalidParameter;
2562
2563     rectsF = GdipAlloc(sizeof(GpRectF)*count);
2564     if(!rectsF)
2565         return OutOfMemory;
2566
2567     for(i = 0; i < count; i++){
2568         rectsF[i].X      = (REAL)rects[i].X;
2569         rectsF[i].Y      = (REAL)rects[i].Y;
2570         rectsF[i].X      = (REAL)rects[i].Width;
2571         rectsF[i].Height = (REAL)rects[i].Height;
2572     }
2573
2574     ret = GdipFillRectangles(graphics,brush,rectsF,count);
2575     GdipFree(rectsF);
2576
2577     return ret;
2578 }
2579
2580 /*****************************************************************************
2581  * GdipFillRegion [GDIPLUS.@]
2582  */
2583 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2584         GpRegion* region)
2585 {
2586     INT save_state;
2587     GpStatus status;
2588     HRGN hrgn;
2589
2590     TRACE("(%p, %p, %p)\n", graphics, brush, region);
2591
2592     if (!(graphics && brush && region))
2593         return InvalidParameter;
2594
2595     if(graphics->busy)
2596         return ObjectBusy;
2597
2598     status = GdipGetRegionHRgn(region, graphics, &hrgn);
2599     if(status != Ok)
2600         return status;
2601
2602     save_state = SaveDC(graphics->hdc);
2603     EndPath(graphics->hdc);
2604     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2605
2606     FillRgn(graphics->hdc, hrgn, brush->gdibrush);
2607
2608     RestoreDC(graphics->hdc, save_state);
2609
2610     DeleteObject(hrgn);
2611
2612     return Ok;
2613 }
2614
2615 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2616 {
2617     static int calls;
2618
2619     if(!graphics)
2620         return InvalidParameter;
2621
2622     if(graphics->busy)
2623         return ObjectBusy;
2624
2625     if(!(calls++))
2626         FIXME("not implemented\n");
2627
2628     return NotImplemented;
2629 }
2630
2631 /*****************************************************************************
2632  * GdipGetClipBounds [GDIPLUS.@]
2633  */
2634 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
2635 {
2636     TRACE("(%p, %p)\n", graphics, rect);
2637
2638     if(!graphics)
2639         return InvalidParameter;
2640
2641     if(graphics->busy)
2642         return ObjectBusy;
2643
2644     return GdipGetRegionBounds(graphics->clip, graphics, rect);
2645 }
2646
2647 /*****************************************************************************
2648  * GdipGetClipBoundsI [GDIPLUS.@]
2649  */
2650 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
2651 {
2652     TRACE("(%p, %p)\n", graphics, rect);
2653
2654     if(!graphics)
2655         return InvalidParameter;
2656
2657     if(graphics->busy)
2658         return ObjectBusy;
2659
2660     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
2661 }
2662
2663 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2664 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2665     CompositingMode *mode)
2666 {
2667     TRACE("(%p, %p)\n", graphics, mode);
2668
2669     if(!graphics || !mode)
2670         return InvalidParameter;
2671
2672     if(graphics->busy)
2673         return ObjectBusy;
2674
2675     *mode = graphics->compmode;
2676
2677     return Ok;
2678 }
2679
2680 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2681 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2682     CompositingQuality *quality)
2683 {
2684     TRACE("(%p, %p)\n", graphics, quality);
2685
2686     if(!graphics || !quality)
2687         return InvalidParameter;
2688
2689     if(graphics->busy)
2690         return ObjectBusy;
2691
2692     *quality = graphics->compqual;
2693
2694     return Ok;
2695 }
2696
2697 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2698 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2699     InterpolationMode *mode)
2700 {
2701     TRACE("(%p, %p)\n", graphics, mode);
2702
2703     if(!graphics || !mode)
2704         return InvalidParameter;
2705
2706     if(graphics->busy)
2707         return ObjectBusy;
2708
2709     *mode = graphics->interpolation;
2710
2711     return Ok;
2712 }
2713
2714 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2715 {
2716     if(!graphics || !argb)
2717         return InvalidParameter;
2718
2719     if(graphics->busy)
2720         return ObjectBusy;
2721
2722     FIXME("(%p, %p): stub\n", graphics, argb);
2723
2724     return NotImplemented;
2725 }
2726
2727 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2728 {
2729     TRACE("(%p, %p)\n", graphics, scale);
2730
2731     if(!graphics || !scale)
2732         return InvalidParameter;
2733
2734     if(graphics->busy)
2735         return ObjectBusy;
2736
2737     *scale = graphics->scale;
2738
2739     return Ok;
2740 }
2741
2742 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2743 {
2744     TRACE("(%p, %p)\n", graphics, unit);
2745
2746     if(!graphics || !unit)
2747         return InvalidParameter;
2748
2749     if(graphics->busy)
2750         return ObjectBusy;
2751
2752     *unit = graphics->unit;
2753
2754     return Ok;
2755 }
2756
2757 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2758 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2759     *mode)
2760 {
2761     TRACE("(%p, %p)\n", graphics, mode);
2762
2763     if(!graphics || !mode)
2764         return InvalidParameter;
2765
2766     if(graphics->busy)
2767         return ObjectBusy;
2768
2769     *mode = graphics->pixeloffset;
2770
2771     return Ok;
2772 }
2773
2774 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2775 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2776 {
2777     TRACE("(%p, %p)\n", graphics, mode);
2778
2779     if(!graphics || !mode)
2780         return InvalidParameter;
2781
2782     if(graphics->busy)
2783         return ObjectBusy;
2784
2785     *mode = graphics->smoothing;
2786
2787     return Ok;
2788 }
2789
2790 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2791 {
2792     TRACE("(%p, %p)\n", graphics, contrast);
2793
2794     if(!graphics || !contrast)
2795         return InvalidParameter;
2796
2797     *contrast = graphics->textcontrast;
2798
2799     return Ok;
2800 }
2801
2802 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2803 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2804     TextRenderingHint *hint)
2805 {
2806     TRACE("(%p, %p)\n", graphics, hint);
2807
2808     if(!graphics || !hint)
2809         return InvalidParameter;
2810
2811     if(graphics->busy)
2812         return ObjectBusy;
2813
2814     *hint = graphics->texthint;
2815
2816     return Ok;
2817 }
2818
2819 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2820 {
2821     TRACE("(%p, %p)\n", graphics, matrix);
2822
2823     if(!graphics || !matrix)
2824         return InvalidParameter;
2825
2826     if(graphics->busy)
2827         return ObjectBusy;
2828
2829     *matrix = *graphics->worldtrans;
2830     return Ok;
2831 }
2832
2833 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2834 {
2835     GpSolidFill *brush;
2836     GpStatus stat;
2837     RECT rect;
2838
2839     TRACE("(%p, %x)\n", graphics, color);
2840
2841     if(!graphics)
2842         return InvalidParameter;
2843
2844     if(graphics->busy)
2845         return ObjectBusy;
2846
2847     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2848         return stat;
2849
2850     if(graphics->hwnd){
2851         if(!GetWindowRect(graphics->hwnd, &rect)){
2852             GdipDeleteBrush((GpBrush*)brush);
2853             return GenericError;
2854         }
2855
2856         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right  - rect.left),
2857                                                                (REAL)(rect.bottom - rect.top));
2858     }
2859     else
2860         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2861                                                                (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2862
2863     GdipDeleteBrush((GpBrush*)brush);
2864
2865     return Ok;
2866 }
2867
2868 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2869 {
2870     TRACE("(%p, %p)\n", graphics, res);
2871
2872     if(!graphics || !res)
2873         return InvalidParameter;
2874
2875     return GdipIsEmptyRegion(graphics->clip, graphics, res);
2876 }
2877
2878 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2879 {
2880     FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2881
2882     if(!graphics || !result)
2883         return InvalidParameter;
2884
2885     if(graphics->busy)
2886         return ObjectBusy;
2887
2888     return NotImplemented;
2889 }
2890
2891 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2892 {
2893     FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2894
2895     if(!graphics || !result)
2896         return InvalidParameter;
2897
2898     if(graphics->busy)
2899         return ObjectBusy;
2900
2901     return NotImplemented;
2902 }
2903
2904 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2905         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2906         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2907         INT regionCount, GpRegion** regions)
2908 {
2909     if (!(graphics && string && font && layoutRect && stringFormat && regions))
2910         return InvalidParameter;
2911
2912     FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2913             length, font, layoutRect, stringFormat, regionCount, regions);
2914
2915     return NotImplemented;
2916 }
2917
2918 /* Find the smallest rectangle that bounds the text when it is printed in rect
2919  * according to the format options listed in format. If rect has 0 width and
2920  * height, then just find the smallest rectangle that bounds the text when it's
2921  * printed at location (rect->X, rect-Y). */
2922 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2923     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2924     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2925     INT *codepointsfitted, INT *linesfilled)
2926 {
2927     HFONT oldfont;
2928     WCHAR* stringdup;
2929     INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2930         nheight;
2931     SIZE size;
2932
2933     if(!graphics || !string || !font || !rect)
2934         return InvalidParameter;
2935
2936     if(linesfilled) *linesfilled = 0;
2937     if(codepointsfitted) *codepointsfitted = 0;
2938
2939     if(format)
2940         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2941
2942     if(length == -1) length = lstrlenW(string);
2943
2944     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2945     if(!stringdup) return OutOfMemory;
2946
2947     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2948     nwidth = roundr(rect->Width);
2949     nheight = roundr(rect->Height);
2950
2951     if((nwidth == 0) && (nheight == 0))
2952         nwidth = nheight = INT_MAX;
2953
2954     for(i = 0, j = 0; i < length; i++){
2955         if(!isprintW(string[i]) && (string[i] != '\n'))
2956             continue;
2957
2958         stringdup[j] = string[i];
2959         j++;
2960     }
2961
2962     stringdup[j] = 0;
2963     length = j;
2964
2965     while(sum < length){
2966         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2967                               nwidth, &fit, NULL, &size);
2968         fitcpy = fit;
2969
2970         if(fit == 0)
2971             break;
2972
2973         for(lret = 0; lret < fit; lret++)
2974             if(*(stringdup + sum + lret) == '\n')
2975                 break;
2976
2977         /* Line break code (may look strange, but it imitates windows). */
2978         if(lret < fit)
2979             fit = lret;    /* this is not an off-by-one error */
2980         else if(fit < (length - sum)){
2981             if(*(stringdup + sum + fit) == ' ')
2982                 while(*(stringdup + sum + fit) == ' ')
2983                     fit++;
2984             else
2985                 while(*(stringdup + sum + fit - 1) != ' '){
2986                     fit--;
2987
2988                     if(*(stringdup + sum + fit) == '\t')
2989                         break;
2990
2991                     if(fit == 0){
2992                         fit = fitcpy;
2993                         break;
2994                     }
2995                 }
2996         }
2997
2998         GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2999                               nwidth, &j, NULL, &size);
3000
3001         sum += fit + (lret < fitcpy ? 1 : 0);
3002         if(codepointsfitted) *codepointsfitted = sum;
3003
3004         height += size.cy;
3005         if(linesfilled) *linesfilled += size.cy;
3006         max_width = max(max_width, size.cx);
3007
3008         if(height > nheight)
3009             break;
3010
3011         /* Stop if this was a linewrap (but not if it was a linebreak). */
3012         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3013             break;
3014     }
3015
3016     bounds->X = rect->X;
3017     bounds->Y = rect->Y;
3018     bounds->Width = (REAL)max_width;
3019     bounds->Height = (REAL) min(height, nheight);
3020
3021     GdipFree(stringdup);
3022     DeleteObject(SelectObject(graphics->hdc, oldfont));
3023
3024     return Ok;
3025 }
3026
3027 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3028 {
3029     TRACE("(%p)\n", graphics);
3030
3031     if(!graphics)
3032         return InvalidParameter;
3033
3034     if(graphics->busy)
3035         return ObjectBusy;
3036
3037     return GdipSetInfinite(graphics->clip);
3038 }
3039
3040 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3041 {
3042     TRACE("(%p)\n", graphics);
3043
3044     if(!graphics)
3045         return InvalidParameter;
3046
3047     if(graphics->busy)
3048         return ObjectBusy;
3049
3050     graphics->worldtrans->matrix[0] = 1.0;
3051     graphics->worldtrans->matrix[1] = 0.0;
3052     graphics->worldtrans->matrix[2] = 0.0;
3053     graphics->worldtrans->matrix[3] = 1.0;
3054     graphics->worldtrans->matrix[4] = 0.0;
3055     graphics->worldtrans->matrix[5] = 0.0;
3056
3057     return Ok;
3058 }
3059
3060 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3061 {
3062     static int calls;
3063
3064     if(!graphics)
3065         return InvalidParameter;
3066
3067     if(!(calls++))
3068         FIXME("graphics state not implemented\n");
3069
3070     return Ok;
3071 }
3072
3073 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3074     GpMatrixOrder order)
3075 {
3076     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3077
3078     if(!graphics)
3079         return InvalidParameter;
3080
3081     if(graphics->busy)
3082         return ObjectBusy;
3083
3084     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3085 }
3086
3087 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3088 {
3089     static int calls;
3090
3091     if(!graphics || !state)
3092         return InvalidParameter;
3093
3094     if(!(calls++))
3095         FIXME("graphics state not implemented\n");
3096
3097     *state = 0xdeadbeef;
3098     return Ok;
3099 }
3100
3101 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
3102 {
3103     FIXME("(%p, %p)\n", graphics, state);
3104
3105     if(!graphics || !state)
3106         return InvalidParameter;
3107
3108     *state = 0xdeadbeef;
3109     return Ok;
3110 }
3111
3112 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
3113 {
3114     FIXME("(%p, 0x%x)\n", graphics, state);
3115
3116     if(!graphics || !state)
3117         return InvalidParameter;
3118
3119     return Ok;
3120 }
3121
3122 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
3123     REAL sy, GpMatrixOrder order)
3124 {
3125     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
3126
3127     if(!graphics)
3128         return InvalidParameter;
3129
3130     if(graphics->busy)
3131         return ObjectBusy;
3132
3133     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
3134 }
3135
3136 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
3137     CombineMode mode)
3138 {
3139     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
3140
3141     if(!graphics || !srcgraphics)
3142         return InvalidParameter;
3143
3144     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
3145 }
3146
3147 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
3148     CompositingMode mode)
3149 {
3150     TRACE("(%p, %d)\n", graphics, mode);
3151
3152     if(!graphics)
3153         return InvalidParameter;
3154
3155     if(graphics->busy)
3156         return ObjectBusy;
3157
3158     graphics->compmode = mode;
3159
3160     return Ok;
3161 }
3162
3163 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
3164     CompositingQuality quality)
3165 {
3166     TRACE("(%p, %d)\n", graphics, quality);
3167
3168     if(!graphics)
3169         return InvalidParameter;
3170
3171     if(graphics->busy)
3172         return ObjectBusy;
3173
3174     graphics->compqual = quality;
3175
3176     return Ok;
3177 }
3178
3179 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
3180     InterpolationMode mode)
3181 {
3182     TRACE("(%p, %d)\n", graphics, mode);
3183
3184     if(!graphics)
3185         return InvalidParameter;
3186
3187     if(graphics->busy)
3188         return ObjectBusy;
3189
3190     graphics->interpolation = mode;
3191
3192     return Ok;
3193 }
3194
3195 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
3196 {
3197     TRACE("(%p, %.2f)\n", graphics, scale);
3198
3199     if(!graphics || (scale <= 0.0))
3200         return InvalidParameter;
3201
3202     if(graphics->busy)
3203         return ObjectBusy;
3204
3205     graphics->scale = scale;
3206
3207     return Ok;
3208 }
3209
3210 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
3211 {
3212     TRACE("(%p, %d)\n", graphics, unit);
3213
3214     if(!graphics)
3215         return InvalidParameter;
3216
3217     if(graphics->busy)
3218         return ObjectBusy;
3219
3220     if(unit == UnitWorld)
3221         return InvalidParameter;
3222
3223     graphics->unit = unit;
3224
3225     return Ok;
3226 }
3227
3228 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3229     mode)
3230 {
3231     TRACE("(%p, %d)\n", graphics, mode);
3232
3233     if(!graphics)
3234         return InvalidParameter;
3235
3236     if(graphics->busy)
3237         return ObjectBusy;
3238
3239     graphics->pixeloffset = mode;
3240
3241     return Ok;
3242 }
3243
3244 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
3245 {
3246     TRACE("(%p, %d)\n", graphics, mode);
3247
3248     if(!graphics)
3249         return InvalidParameter;
3250
3251     if(graphics->busy)
3252         return ObjectBusy;
3253
3254     graphics->smoothing = mode;
3255
3256     return Ok;
3257 }
3258
3259 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3260 {
3261     TRACE("(%p, %d)\n", graphics, contrast);
3262
3263     if(!graphics)
3264         return InvalidParameter;
3265
3266     graphics->textcontrast = contrast;
3267
3268     return Ok;
3269 }
3270
3271 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3272     TextRenderingHint hint)
3273 {
3274     TRACE("(%p, %d)\n", graphics, hint);
3275
3276     if(!graphics)
3277         return InvalidParameter;
3278
3279     if(graphics->busy)
3280         return ObjectBusy;
3281
3282     graphics->texthint = hint;
3283
3284     return Ok;
3285 }
3286
3287 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3288 {
3289     TRACE("(%p, %p)\n", graphics, matrix);
3290
3291     if(!graphics || !matrix)
3292         return InvalidParameter;
3293
3294     if(graphics->busy)
3295         return ObjectBusy;
3296
3297     GdipDeleteMatrix(graphics->worldtrans);
3298     return GdipCloneMatrix(matrix, &graphics->worldtrans);
3299 }
3300
3301 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3302     REAL dy, GpMatrixOrder order)
3303 {
3304     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3305
3306     if(!graphics)
3307         return InvalidParameter;
3308
3309     if(graphics->busy)
3310         return ObjectBusy;
3311
3312     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3313 }
3314
3315 /*****************************************************************************
3316  * GdipSetClipHrgn [GDIPLUS.@]
3317  */
3318 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
3319 {
3320     GpRegion *region;
3321     GpStatus status;
3322
3323     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
3324
3325     if(!graphics)
3326         return InvalidParameter;
3327
3328     status = GdipCreateRegionHrgn(hrgn, &region);
3329     if(status != Ok)
3330         return status;
3331
3332     status = GdipSetClipRegion(graphics, region, mode);
3333
3334     GdipDeleteRegion(region);
3335     return status;
3336 }
3337
3338 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3339 {
3340     TRACE("(%p, %p, %d)\n", graphics, path, mode);
3341
3342     if(!graphics)
3343         return InvalidParameter;
3344
3345     if(graphics->busy)
3346         return ObjectBusy;
3347
3348     return GdipCombineRegionPath(graphics->clip, path, mode);
3349 }
3350
3351 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3352                                     REAL width, REAL height,
3353                                     CombineMode mode)
3354 {
3355     GpRectF rect;
3356
3357     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3358
3359     if(!graphics)
3360         return InvalidParameter;
3361
3362     if(graphics->busy)
3363         return ObjectBusy;
3364
3365     rect.X = x;
3366     rect.Y = y;
3367     rect.Width  = width;
3368     rect.Height = height;
3369
3370     return GdipCombineRegionRect(graphics->clip, &rect, mode);
3371 }
3372
3373 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3374                                      INT width, INT height,
3375                                      CombineMode mode)
3376 {
3377     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3378
3379     if(!graphics)
3380         return InvalidParameter;
3381
3382     if(graphics->busy)
3383         return ObjectBusy;
3384
3385     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3386 }
3387
3388 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3389                                       CombineMode mode)
3390 {
3391     TRACE("(%p, %p, %d)\n", graphics, region, mode);
3392
3393     if(!graphics || !region)
3394         return InvalidParameter;
3395
3396     if(graphics->busy)
3397         return ObjectBusy;
3398
3399     return GdipCombineRegionRegion(graphics->clip, region, mode);
3400 }
3401
3402 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3403     UINT limitDpi)
3404 {
3405     static int calls;
3406
3407     if(!(calls++))
3408         FIXME("not implemented\n");
3409
3410     return NotImplemented;
3411 }
3412
3413 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3414     INT count)
3415 {
3416     INT save_state;
3417     POINT *pti;
3418
3419     TRACE("(%p, %p, %d)\n", graphics, points, count);
3420
3421     if(!graphics || !pen || count<=0)
3422         return InvalidParameter;
3423
3424     if(graphics->busy)
3425         return ObjectBusy;
3426
3427     pti = GdipAlloc(sizeof(POINT) * count);
3428
3429     save_state = prepare_dc(graphics, pen);
3430     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3431
3432     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3433     Polygon(graphics->hdc, pti, count);
3434
3435     restore_dc(graphics, save_state);
3436     GdipFree(pti);
3437
3438     return Ok;
3439 }
3440
3441 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3442     INT count)
3443 {
3444     GpStatus ret;
3445     GpPointF *ptf;
3446     INT i;
3447
3448     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3449
3450     if(count<=0)    return InvalidParameter;
3451     ptf = GdipAlloc(sizeof(GpPointF) * count);
3452
3453     for(i = 0;i < count; i++){
3454         ptf[i].X = (REAL)points[i].X;
3455         ptf[i].Y = (REAL)points[i].Y;
3456     }
3457
3458     ret = GdipDrawPolygon(graphics,pen,ptf,count);
3459     GdipFree(ptf);
3460
3461     return ret;
3462 }
3463
3464 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3465 {
3466     TRACE("(%p, %p)\n", graphics, dpi);
3467
3468     if(!graphics || !dpi)
3469         return InvalidParameter;
3470
3471     if(graphics->busy)
3472         return ObjectBusy;
3473
3474     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3475
3476     return Ok;
3477 }
3478
3479 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3480 {
3481     TRACE("(%p, %p)\n", graphics, dpi);
3482
3483     if(!graphics || !dpi)
3484         return InvalidParameter;
3485
3486     if(graphics->busy)
3487         return ObjectBusy;
3488
3489     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3490
3491     return Ok;
3492 }
3493
3494 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3495     GpMatrixOrder order)
3496 {
3497     GpMatrix m;
3498     GpStatus ret;
3499
3500     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3501
3502     if(!graphics || !matrix)
3503         return InvalidParameter;
3504
3505     if(graphics->busy)
3506         return ObjectBusy;
3507
3508     m = *(graphics->worldtrans);
3509
3510     ret = GdipMultiplyMatrix(&m, matrix, order);
3511     if(ret == Ok)
3512         *(graphics->worldtrans) = m;
3513
3514     return ret;
3515 }
3516
3517 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3518 {
3519     TRACE("(%p, %p)\n", graphics, hdc);
3520
3521     if(!graphics || !hdc)
3522         return InvalidParameter;
3523
3524     if(graphics->busy)
3525         return ObjectBusy;
3526
3527     *hdc = graphics->hdc;
3528     graphics->busy = TRUE;
3529
3530     return Ok;
3531 }
3532
3533 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3534 {
3535     TRACE("(%p, %p)\n", graphics, hdc);
3536
3537     if(!graphics)
3538         return InvalidParameter;
3539
3540     if(graphics->hdc != hdc || !(graphics->busy))
3541         return InvalidParameter;
3542
3543     graphics->busy = FALSE;
3544
3545     return Ok;
3546 }
3547
3548 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3549 {
3550     GpRegion *clip;
3551     GpStatus status;
3552
3553     TRACE("(%p, %p)\n", graphics, region);
3554
3555     if(!graphics || !region)
3556         return InvalidParameter;
3557
3558     if(graphics->busy)
3559         return ObjectBusy;
3560
3561     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3562         return status;
3563
3564     /* free everything except root node and header */
3565     delete_element(&region->node);
3566     memcpy(region, clip, sizeof(GpRegion));
3567
3568     return Ok;
3569 }
3570
3571 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3572                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
3573 {
3574     if(!graphics || !points || count <= 0)
3575         return InvalidParameter;
3576
3577     if(graphics->busy)
3578         return ObjectBusy;
3579
3580     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3581
3582     return NotImplemented;
3583 }
3584
3585 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3586                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
3587 {
3588     FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3589
3590     return NotImplemented;
3591 }
3592
3593 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3594 {
3595     FIXME("\n");
3596
3597     return NULL;
3598 }
3599
3600 /*****************************************************************************
3601  * GdipTranslateClip [GDIPLUS.@]
3602  */
3603 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
3604 {
3605     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
3606
3607     if(!graphics)
3608         return InvalidParameter;
3609
3610     if(graphics->busy)
3611         return ObjectBusy;
3612
3613     return GdipTranslateRegion(graphics->clip, dx, dy);
3614 }
3615
3616 /*****************************************************************************
3617  * GdipTranslateClipI [GDIPLUS.@]
3618  */
3619 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
3620 {
3621     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
3622
3623     if(!graphics)
3624         return InvalidParameter;
3625
3626     if(graphics->busy)
3627         return ObjectBusy;
3628
3629     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
3630 }