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