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