gdiplus: Don't crash when a NULL format is passed to GdipDrawString.
[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 handle 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)
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)
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 GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
1536     GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
1537     REAL tension)
1538 {
1539     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1540
1541     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1542         return InvalidParameter;
1543     }
1544
1545     return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
1546 }
1547
1548 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
1549     GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
1550     REAL tension)
1551 {
1552     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1553
1554     if(count < 0){
1555         return OutOfMemory;
1556     }
1557
1558     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1559         return InvalidParameter;
1560     }
1561
1562     return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
1563 }
1564
1565 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1566     REAL y, REAL width, REAL height)
1567 {
1568     INT save_state;
1569     GpPointF ptf[2];
1570     POINT pti[2];
1571
1572     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1573
1574     if(!graphics || !pen)
1575         return InvalidParameter;
1576
1577     if(graphics->busy)
1578         return ObjectBusy;
1579
1580     ptf[0].X = x;
1581     ptf[0].Y = y;
1582     ptf[1].X = x + width;
1583     ptf[1].Y = y + height;
1584
1585     save_state = prepare_dc(graphics, pen);
1586     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1587
1588     transform_and_round_points(graphics, pti, ptf, 2);
1589
1590     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1591
1592     restore_dc(graphics, save_state);
1593
1594     return Ok;
1595 }
1596
1597 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1598     INT y, INT width, INT height)
1599 {
1600     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1601
1602     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1603 }
1604
1605
1606 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1607 {
1608     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1609
1610     /* IPicture::Render uses LONG coords */
1611     return GdipDrawImageI(graphics,image,roundr(x),roundr(y));
1612 }
1613
1614 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1615     INT y)
1616 {
1617     UINT width, height, srcw, srch;
1618
1619     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1620
1621     if(!graphics || !image)
1622         return InvalidParameter;
1623
1624     GdipGetImageWidth(image, &width);
1625     GdipGetImageHeight(image, &height);
1626
1627     srcw = width * (((REAL) INCH_HIMETRIC) /
1628             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1629     srch = height * (((REAL) INCH_HIMETRIC) /
1630             ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1631
1632     if(image->type != ImageTypeMetafile){
1633         y += height;
1634         height *= -1;
1635     }
1636
1637     IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1638                     0, 0, srcw, srch, NULL);
1639
1640     return Ok;
1641 }
1642
1643 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
1644     REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
1645     GpUnit srcUnit)
1646 {
1647     FIXME("(%p, %p, %f, %f, %f, %f, %f, %f, %d): stub\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1648     return NotImplemented;
1649 }
1650
1651 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
1652     INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
1653     GpUnit srcUnit)
1654 {
1655     FIXME("(%p, %p, %d, %d, %d, %d, %d, %d, %d): stub\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1656     return NotImplemented;
1657 }
1658
1659 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
1660     GDIPCONST GpPointF *dstpoints, INT count)
1661 {
1662     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1663     return NotImplemented;
1664 }
1665
1666 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
1667     GDIPCONST GpPoint *dstpoints, INT count)
1668 {
1669     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1670     return NotImplemented;
1671 }
1672
1673 /* FIXME: partially implemented (only works for rectangular parallelograms) */
1674 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1675      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1676      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1677      DrawImageAbort callback, VOID * callbackData)
1678 {
1679     GpPointF ptf[3];
1680     POINT pti[3];
1681     REAL dx, dy;
1682
1683     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1684           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1685           callbackData);
1686
1687     if(!graphics || !image || !points || count != 3)
1688          return InvalidParameter;
1689
1690     if(srcUnit == UnitInch)
1691         dx = dy = (REAL) INCH_HIMETRIC;
1692     else if(srcUnit == UnitPixel){
1693         dx = ((REAL) INCH_HIMETRIC) /
1694              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1695         dy = ((REAL) INCH_HIMETRIC) /
1696              ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1697     }
1698     else
1699         return NotImplemented;
1700
1701     memcpy(ptf, points, 3 * sizeof(GpPointF));
1702     transform_and_round_points(graphics, pti, ptf, 3);
1703
1704     /* IPicture renders bitmaps with the y-axis reversed
1705      * FIXME: flipping for unknown image type might not be correct. */
1706     if(image->type != ImageTypeMetafile){
1707         INT temp;
1708         temp = pti[0].y;
1709         pti[0].y = pti[2].y;
1710         pti[2].y = temp;
1711     }
1712
1713     if(IPicture_Render(image->picture, graphics->hdc,
1714         pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1715         srcx * dx, srcy * dy,
1716         srcwidth * dx, srcheight * dy,
1717         NULL) != S_OK){
1718         if(callback)
1719             callback(callbackData);
1720         return GenericError;
1721     }
1722
1723     return Ok;
1724 }
1725
1726 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
1727      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
1728      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1729      DrawImageAbort callback, VOID * callbackData)
1730 {
1731     GpPointF pointsF[3];
1732     INT i;
1733
1734     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
1735           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1736           callbackData);
1737
1738     if(!points || count!=3)
1739         return InvalidParameter;
1740
1741     for(i = 0; i < count; i++){
1742         pointsF[i].X = (REAL)points[i].X;
1743         pointsF[i].Y = (REAL)points[i].Y;
1744     }
1745
1746     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
1747                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
1748                                    callback, callbackData);
1749 }
1750
1751 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
1752     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
1753     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
1754     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
1755     VOID * callbackData)
1756 {
1757     GpPointF points[3];
1758
1759     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
1760           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1761           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1762
1763     points[0].X = dstx;
1764     points[0].Y = dsty;
1765     points[1].X = dstx + dstwidth;
1766     points[1].Y = dsty;
1767     points[2].X = dstx;
1768     points[2].Y = dsty + dstheight;
1769
1770     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1771                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1772 }
1773
1774 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
1775         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
1776         INT srcwidth, INT srcheight, GpUnit srcUnit,
1777         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
1778         VOID * callbackData)
1779 {
1780     GpPointF points[3];
1781
1782     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
1783           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1784           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1785
1786     points[0].X = dstx;
1787     points[0].Y = dsty;
1788     points[1].X = dstx + dstwidth;
1789     points[1].Y = dsty;
1790     points[2].X = dstx;
1791     points[2].Y = dsty + dstheight;
1792
1793     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1794                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1795 }
1796
1797 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
1798     REAL x, REAL y, REAL width, REAL height)
1799 {
1800     RectF bounds;
1801     GpUnit unit;
1802     GpStatus ret;
1803
1804     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
1805
1806     if(!graphics || !image)
1807         return InvalidParameter;
1808
1809     ret = GdipGetImageBounds(image, &bounds, &unit);
1810     if(ret != Ok)
1811         return ret;
1812
1813     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
1814                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
1815                                  unit, NULL, NULL, NULL);
1816 }
1817
1818 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
1819     INT x, INT y, INT width, INT height)
1820 {
1821     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
1822
1823     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
1824 }
1825
1826 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1827     REAL y1, REAL x2, REAL y2)
1828 {
1829     INT save_state;
1830     GpPointF pt[2];
1831     GpStatus retval;
1832
1833     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
1834
1835     if(!pen || !graphics)
1836         return InvalidParameter;
1837
1838     if(graphics->busy)
1839         return ObjectBusy;
1840
1841     pt[0].X = x1;
1842     pt[0].Y = y1;
1843     pt[1].X = x2;
1844     pt[1].Y = y2;
1845
1846     save_state = prepare_dc(graphics, pen);
1847
1848     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1849
1850     restore_dc(graphics, save_state);
1851
1852     return retval;
1853 }
1854
1855 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1856     INT y1, INT x2, INT y2)
1857 {
1858     INT save_state;
1859     GpPointF pt[2];
1860     GpStatus retval;
1861
1862     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
1863
1864     if(!pen || !graphics)
1865         return InvalidParameter;
1866
1867     if(graphics->busy)
1868         return ObjectBusy;
1869
1870     pt[0].X = (REAL)x1;
1871     pt[0].Y = (REAL)y1;
1872     pt[1].X = (REAL)x2;
1873     pt[1].Y = (REAL)y2;
1874
1875     save_state = prepare_dc(graphics, pen);
1876
1877     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1878
1879     restore_dc(graphics, save_state);
1880
1881     return retval;
1882 }
1883
1884 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1885     GpPointF *points, INT count)
1886 {
1887     INT save_state;
1888     GpStatus retval;
1889
1890     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1891
1892     if(!pen || !graphics || (count < 2))
1893         return InvalidParameter;
1894
1895     if(graphics->busy)
1896         return ObjectBusy;
1897
1898     save_state = prepare_dc(graphics, pen);
1899
1900     retval = draw_polyline(graphics, pen, points, count, TRUE);
1901
1902     restore_dc(graphics, save_state);
1903
1904     return retval;
1905 }
1906
1907 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
1908     GpPoint *points, INT count)
1909 {
1910     INT save_state;
1911     GpStatus retval;
1912     GpPointF *ptf = NULL;
1913     int i;
1914
1915     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1916
1917     if(!pen || !graphics || (count < 2))
1918         return InvalidParameter;
1919
1920     if(graphics->busy)
1921         return ObjectBusy;
1922
1923     ptf = GdipAlloc(count * sizeof(GpPointF));
1924     if(!ptf) return OutOfMemory;
1925
1926     for(i = 0; i < count; i ++){
1927         ptf[i].X = (REAL) points[i].X;
1928         ptf[i].Y = (REAL) points[i].Y;
1929     }
1930
1931     save_state = prepare_dc(graphics, pen);
1932
1933     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
1934
1935     restore_dc(graphics, save_state);
1936
1937     GdipFree(ptf);
1938     return retval;
1939 }
1940
1941 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1942 {
1943     INT save_state;
1944     GpStatus retval;
1945
1946     TRACE("(%p, %p, %p)\n", graphics, pen, path);
1947
1948     if(!pen || !graphics)
1949         return InvalidParameter;
1950
1951     if(graphics->busy)
1952         return ObjectBusy;
1953
1954     save_state = prepare_dc(graphics, pen);
1955
1956     retval = draw_poly(graphics, pen, path->pathdata.Points,
1957                        path->pathdata.Types, path->pathdata.Count, TRUE);
1958
1959     restore_dc(graphics, save_state);
1960
1961     return retval;
1962 }
1963
1964 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1965     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1966 {
1967     INT save_state;
1968
1969     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1970             width, height, startAngle, sweepAngle);
1971
1972     if(!graphics || !pen)
1973         return InvalidParameter;
1974
1975     if(graphics->busy)
1976         return ObjectBusy;
1977
1978     save_state = prepare_dc(graphics, pen);
1979     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1980
1981     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1982
1983     restore_dc(graphics, save_state);
1984
1985     return Ok;
1986 }
1987
1988 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
1989     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1990 {
1991     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1992             width, height, startAngle, sweepAngle);
1993
1994     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1995 }
1996
1997 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
1998     REAL y, REAL width, REAL height)
1999 {
2000     INT save_state;
2001     GpPointF ptf[4];
2002     POINT pti[4];
2003
2004     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2005
2006     if(!pen || !graphics)
2007         return InvalidParameter;
2008
2009     if(graphics->busy)
2010         return ObjectBusy;
2011
2012     ptf[0].X = x;
2013     ptf[0].Y = y;
2014     ptf[1].X = x + width;
2015     ptf[1].Y = y;
2016     ptf[2].X = x + width;
2017     ptf[2].Y = y + height;
2018     ptf[3].X = x;
2019     ptf[3].Y = y + height;
2020
2021     save_state = prepare_dc(graphics, pen);
2022     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2023
2024     transform_and_round_points(graphics, pti, ptf, 4);
2025     Polygon(graphics->hdc, pti, 4);
2026
2027     restore_dc(graphics, save_state);
2028
2029     return Ok;
2030 }
2031
2032 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
2033     INT y, INT width, INT height)
2034 {
2035     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2036
2037     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2038 }
2039
2040 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
2041     GDIPCONST GpRectF* rects, INT count)
2042 {
2043     GpPointF *ptf;
2044     POINT *pti;
2045     INT save_state, i;
2046
2047     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2048
2049     if(!graphics || !pen || !rects || count < 1)
2050         return InvalidParameter;
2051
2052     if(graphics->busy)
2053         return ObjectBusy;
2054
2055     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
2056     pti = GdipAlloc(4 * count * sizeof(POINT));
2057
2058     if(!ptf || !pti){
2059         GdipFree(ptf);
2060         GdipFree(pti);
2061         return OutOfMemory;
2062     }
2063
2064     for(i = 0; i < count; i++){
2065         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
2066         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
2067         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
2068         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
2069     }
2070
2071     save_state = prepare_dc(graphics, pen);
2072     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2073
2074     transform_and_round_points(graphics, pti, ptf, 4 * count);
2075
2076     for(i = 0; i < count; i++)
2077         Polygon(graphics->hdc, &pti[4 * i], 4);
2078
2079     restore_dc(graphics, save_state);
2080
2081     GdipFree(ptf);
2082     GdipFree(pti);
2083
2084     return Ok;
2085 }
2086
2087 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
2088     GDIPCONST GpRect* rects, INT count)
2089 {
2090     GpRectF *rectsF;
2091     GpStatus ret;
2092     INT i;
2093
2094     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2095
2096     if(!rects || count<=0)
2097         return InvalidParameter;
2098
2099     rectsF = GdipAlloc(sizeof(GpRectF) * count);
2100     if(!rectsF)
2101         return OutOfMemory;
2102
2103     for(i = 0;i < count;i++){
2104         rectsF[i].X      = (REAL)rects[i].X;
2105         rectsF[i].Y      = (REAL)rects[i].Y;
2106         rectsF[i].Width  = (REAL)rects[i].Width;
2107         rectsF[i].Height = (REAL)rects[i].Height;
2108     }
2109
2110     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
2111     GdipFree(rectsF);
2112
2113     return ret;
2114 }
2115
2116 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
2117     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
2118     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
2119 {
2120     HRGN rgn = NULL;
2121     HFONT gdifont;
2122     LOGFONTW lfw;
2123     TEXTMETRICW textmet;
2124     GpPointF pt[2], rectcpy[4];
2125     POINT corners[4];
2126     WCHAR* stringdup;
2127     REAL angle, ang_cos, ang_sin, rel_width, rel_height;
2128     INT sum = 0, height = 0, offsety = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
2129         nheight;
2130     SIZE size;
2131     POINT drawbase;
2132     UINT drawflags;
2133     RECT drawcoord;
2134
2135     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
2136         length, font, debugstr_rectf(rect), format, brush);
2137
2138     if(!graphics || !string || !font || !brush || !rect)
2139         return InvalidParameter;
2140
2141     if((brush->bt != BrushTypeSolidColor)){
2142         FIXME("not implemented for given parameters\n");
2143         return NotImplemented;
2144     }
2145
2146     if(format){
2147         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2148
2149         /* Should be no need to explicitly test for StringAlignmentNear as
2150          * that is default behavior if no alignment is passed. */
2151         if(format->vertalign != StringAlignmentNear){
2152             RectF bounds;
2153             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
2154
2155             if(format->vertalign == StringAlignmentCenter)
2156                 offsety = (rect->Height - bounds.Height) / 2;
2157             else if(format->vertalign == StringAlignmentFar)
2158                 offsety = (rect->Height - bounds.Height);
2159         }
2160     }
2161
2162     if(length == -1) length = lstrlenW(string);
2163
2164     stringdup = GdipAlloc(length * sizeof(WCHAR));
2165     if(!stringdup) return OutOfMemory;
2166
2167     save_state = SaveDC(graphics->hdc);
2168     SetBkMode(graphics->hdc, TRANSPARENT);
2169     SetTextColor(graphics->hdc, brush->lb.lbColor);
2170
2171     rectcpy[3].X = rectcpy[0].X = rect->X;
2172     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
2173     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
2174     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
2175     transform_and_round_points(graphics, corners, rectcpy, 4);
2176
2177     if (roundr(rect->Width) == 0)
2178     {
2179         rel_width = 1.0;
2180         nwidth = INT_MAX;
2181     }
2182     else
2183     {
2184         rel_width = sqrt((corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) +
2185                          (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y))
2186                          / rect->Width;
2187         nwidth = roundr(rel_width * rect->Width);
2188     }
2189
2190     if (roundr(rect->Height) == 0)
2191     {
2192         rel_height = 1.0;
2193         nheight = INT_MAX;
2194     }
2195     else
2196     {
2197         rel_height = sqrt((corners[2].x - corners[1].x) * (corners[2].x - corners[1].x) +
2198                           (corners[2].y - corners[1].y) * (corners[2].y - corners[1].y))
2199                           / rect->Height;
2200         nheight = roundr(rel_height * rect->Height);
2201     }
2202
2203     if (roundr(rect->Width) != 0 && roundr(rect->Height) != 0)
2204     {
2205         /* FIXME: If only the width or only the height is 0, we should probably still clip */
2206         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
2207         SelectClipRgn(graphics->hdc, rgn);
2208     }
2209
2210     /* Use gdi to find the font, then perform transformations on it (height,
2211      * width, angle). */
2212     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2213     GetTextMetricsW(graphics->hdc, &textmet);
2214     lfw = font->lfw;
2215
2216     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
2217     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
2218
2219     pt[0].X = 0.0;
2220     pt[0].Y = 0.0;
2221     pt[1].X = 1.0;
2222     pt[1].Y = 0.0;
2223     GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
2224     angle = gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
2225     ang_cos = cos(angle);
2226     ang_sin = sin(angle);
2227     lfw.lfEscapement = lfw.lfOrientation = -roundr((angle / M_PI) * 1800.0);
2228
2229     gdifont = CreateFontIndirectW(&lfw);
2230     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
2231
2232     for(i = 0, j = 0; i < length; i++){
2233         if(!isprintW(string[i]) && (string[i] != '\n'))
2234             continue;
2235
2236         stringdup[j] = string[i];
2237         j++;
2238     }
2239
2240     length = j;
2241
2242     if (!format || format->align == StringAlignmentNear)
2243     {
2244         drawbase.x = corners[0].x;
2245         drawbase.y = corners[0].y;
2246         drawflags = DT_NOCLIP | DT_EXPANDTABS;
2247     }
2248     else if (format->align == StringAlignmentCenter)
2249     {
2250         drawbase.x = (corners[0].x + corners[1].x)/2;
2251         drawbase.y = (corners[0].y + corners[1].y)/2;
2252         drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
2253     }
2254     else /* (format->align == StringAlignmentFar) */
2255     {
2256         drawbase.x = corners[1].x;
2257         drawbase.y = corners[1].y;
2258         drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
2259     }
2260
2261     while(sum < length){
2262         drawcoord.left = drawcoord.right = drawbase.x + roundr(ang_sin * (REAL) height);
2263         drawcoord.top = drawcoord.bottom = drawbase.y + roundr(ang_cos * (REAL) height);
2264
2265         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2266                               nwidth, &fit, NULL, &size);
2267         fitcpy = fit;
2268
2269         if(fit == 0){
2270             DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, drawflags);
2271             break;
2272         }
2273
2274         for(lret = 0; lret < fit; lret++)
2275             if(*(stringdup + sum + lret) == '\n')
2276                 break;
2277
2278         /* Line break code (may look strange, but it imitates windows). */
2279         if(lret < fit)
2280             fit = lret;    /* this is not an off-by-one error */
2281         else if(fit < (length - sum)){
2282             if(*(stringdup + sum + fit) == ' ')
2283                 while(*(stringdup + sum + fit) == ' ')
2284                     fit++;
2285             else
2286                 while(*(stringdup + sum + fit - 1) != ' '){
2287                     fit--;
2288
2289                     if(*(stringdup + sum + fit) == '\t')
2290                         break;
2291
2292                     if(fit == 0){
2293                         fit = fitcpy;
2294                         break;
2295                     }
2296                 }
2297         }
2298         DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
2299                   &drawcoord, drawflags);
2300
2301         sum += fit + (lret < fitcpy ? 1 : 0);
2302         height += size.cy;
2303
2304         if(height > nheight)
2305             break;
2306
2307         /* Stop if this was a linewrap (but not if it was a linebreak). */
2308         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2309             break;
2310     }
2311
2312     GdipFree(stringdup);
2313     DeleteObject(rgn);
2314     DeleteObject(gdifont);
2315
2316     RestoreDC(graphics->hdc, save_state);
2317
2318     return Ok;
2319 }
2320
2321 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2322     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2323 {
2324     GpPath *path;
2325     GpStatus stat;
2326
2327     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2328             count, tension, fill);
2329
2330     if(!graphics || !brush || !points)
2331         return InvalidParameter;
2332
2333     if(graphics->busy)
2334         return ObjectBusy;
2335
2336     stat = GdipCreatePath(fill, &path);
2337     if(stat != Ok)
2338         return stat;
2339
2340     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2341     if(stat != Ok){
2342         GdipDeletePath(path);
2343         return stat;
2344     }
2345
2346     stat = GdipFillPath(graphics, brush, path);
2347     if(stat != Ok){
2348         GdipDeletePath(path);
2349         return stat;
2350     }
2351
2352     GdipDeletePath(path);
2353
2354     return Ok;
2355 }
2356
2357 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2358     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2359 {
2360     GpPointF *ptf;
2361     GpStatus stat;
2362     INT i;
2363
2364     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2365             count, tension, fill);
2366
2367     if(!points || count <= 0)
2368         return InvalidParameter;
2369
2370     ptf = GdipAlloc(sizeof(GpPointF)*count);
2371     if(!ptf)
2372         return OutOfMemory;
2373
2374     for(i = 0;i < count;i++){
2375         ptf[i].X = (REAL)points[i].X;
2376         ptf[i].Y = (REAL)points[i].Y;
2377     }
2378
2379     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2380
2381     GdipFree(ptf);
2382
2383     return stat;
2384 }
2385
2386 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2387     REAL y, REAL width, REAL height)
2388 {
2389     INT save_state;
2390     GpPointF ptf[2];
2391     POINT pti[2];
2392
2393     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2394
2395     if(!graphics || !brush)
2396         return InvalidParameter;
2397
2398     if(graphics->busy)
2399         return ObjectBusy;
2400
2401     ptf[0].X = x;
2402     ptf[0].Y = y;
2403     ptf[1].X = x + width;
2404     ptf[1].Y = y + height;
2405
2406     save_state = SaveDC(graphics->hdc);
2407     EndPath(graphics->hdc);
2408     SelectObject(graphics->hdc, brush->gdibrush);
2409     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2410
2411     transform_and_round_points(graphics, pti, ptf, 2);
2412
2413     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2414
2415     RestoreDC(graphics->hdc, save_state);
2416
2417     return Ok;
2418 }
2419
2420 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2421     INT y, INT width, INT height)
2422 {
2423     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2424
2425     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2426 }
2427
2428 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2429 {
2430     INT save_state;
2431     GpStatus retval;
2432
2433     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2434
2435     if(!brush || !graphics || !path)
2436         return InvalidParameter;
2437
2438     if(graphics->busy)
2439         return ObjectBusy;
2440
2441     save_state = SaveDC(graphics->hdc);
2442     EndPath(graphics->hdc);
2443     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2444                                                                     : WINDING));
2445
2446     BeginPath(graphics->hdc);
2447     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2448                        path->pathdata.Types, path->pathdata.Count, FALSE);
2449
2450     if(retval != Ok)
2451         goto end;
2452
2453     EndPath(graphics->hdc);
2454     brush_fill_path(graphics, brush);
2455
2456     retval = Ok;
2457
2458 end:
2459     RestoreDC(graphics->hdc, save_state);
2460
2461     return retval;
2462 }
2463
2464 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2465     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2466 {
2467     INT save_state;
2468
2469     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2470             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2471
2472     if(!graphics || !brush)
2473         return InvalidParameter;
2474
2475     if(graphics->busy)
2476         return ObjectBusy;
2477
2478     save_state = SaveDC(graphics->hdc);
2479     EndPath(graphics->hdc);
2480     SelectObject(graphics->hdc, brush->gdibrush);
2481     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2482
2483     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2484
2485     RestoreDC(graphics->hdc, save_state);
2486
2487     return Ok;
2488 }
2489
2490 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2491     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2492 {
2493     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2494             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2495
2496     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2497 }
2498
2499 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2500     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2501 {
2502     INT save_state;
2503     GpPointF *ptf = NULL;
2504     POINT *pti = NULL;
2505     GpStatus retval = Ok;
2506
2507     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2508
2509     if(!graphics || !brush || !points || !count)
2510         return InvalidParameter;
2511
2512     if(graphics->busy)
2513         return ObjectBusy;
2514
2515     ptf = GdipAlloc(count * sizeof(GpPointF));
2516     pti = GdipAlloc(count * sizeof(POINT));
2517     if(!ptf || !pti){
2518         retval = OutOfMemory;
2519         goto end;
2520     }
2521
2522     memcpy(ptf, points, count * sizeof(GpPointF));
2523
2524     save_state = SaveDC(graphics->hdc);
2525     EndPath(graphics->hdc);
2526     SelectObject(graphics->hdc, brush->gdibrush);
2527     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2528     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2529                                                                   : WINDING));
2530
2531     transform_and_round_points(graphics, pti, ptf, count);
2532     Polygon(graphics->hdc, pti, count);
2533
2534     RestoreDC(graphics->hdc, save_state);
2535
2536 end:
2537     GdipFree(ptf);
2538     GdipFree(pti);
2539
2540     return retval;
2541 }
2542
2543 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2544     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2545 {
2546     INT save_state, i;
2547     GpPointF *ptf = NULL;
2548     POINT *pti = NULL;
2549     GpStatus retval = Ok;
2550
2551     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2552
2553     if(!graphics || !brush || !points || !count)
2554         return InvalidParameter;
2555
2556     if(graphics->busy)
2557         return ObjectBusy;
2558
2559     ptf = GdipAlloc(count * sizeof(GpPointF));
2560     pti = GdipAlloc(count * sizeof(POINT));
2561     if(!ptf || !pti){
2562         retval = OutOfMemory;
2563         goto end;
2564     }
2565
2566     for(i = 0; i < count; i ++){
2567         ptf[i].X = (REAL) points[i].X;
2568         ptf[i].Y = (REAL) points[i].Y;
2569     }
2570
2571     save_state = SaveDC(graphics->hdc);
2572     EndPath(graphics->hdc);
2573     SelectObject(graphics->hdc, brush->gdibrush);
2574     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2575     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2576                                                                   : WINDING));
2577
2578     transform_and_round_points(graphics, pti, ptf, count);
2579     Polygon(graphics->hdc, pti, count);
2580
2581     RestoreDC(graphics->hdc, save_state);
2582
2583 end:
2584     GdipFree(ptf);
2585     GdipFree(pti);
2586
2587     return retval;
2588 }
2589
2590 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2591     GDIPCONST GpPointF *points, INT count)
2592 {
2593     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2594
2595     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2596 }
2597
2598 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2599     GDIPCONST GpPoint *points, INT count)
2600 {
2601     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2602
2603     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2604 }
2605
2606 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2607     REAL x, REAL y, REAL width, REAL height)
2608 {
2609     INT save_state;
2610     GpPointF ptf[4];
2611     POINT pti[4];
2612
2613     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2614
2615     if(!graphics || !brush)
2616         return InvalidParameter;
2617
2618     if(graphics->busy)
2619         return ObjectBusy;
2620
2621     ptf[0].X = x;
2622     ptf[0].Y = y;
2623     ptf[1].X = x + width;
2624     ptf[1].Y = y;
2625     ptf[2].X = x + width;
2626     ptf[2].Y = y + height;
2627     ptf[3].X = x;
2628     ptf[3].Y = y + height;
2629
2630     save_state = SaveDC(graphics->hdc);
2631     EndPath(graphics->hdc);
2632
2633     transform_and_round_points(graphics, pti, ptf, 4);
2634
2635     BeginPath(graphics->hdc);
2636     Polygon(graphics->hdc, pti, 4);
2637     EndPath(graphics->hdc);
2638
2639     brush_fill_path(graphics, brush);
2640
2641     RestoreDC(graphics->hdc, save_state);
2642
2643     return Ok;
2644 }
2645
2646 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2647     INT x, INT y, INT width, INT height)
2648 {
2649     INT save_state;
2650     GpPointF ptf[4];
2651     POINT pti[4];
2652
2653     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2654
2655     if(!graphics || !brush)
2656         return InvalidParameter;
2657
2658     if(graphics->busy)
2659         return ObjectBusy;
2660
2661     ptf[0].X = x;
2662     ptf[0].Y = y;
2663     ptf[1].X = x + width;
2664     ptf[1].Y = y;
2665     ptf[2].X = x + width;
2666     ptf[2].Y = y + height;
2667     ptf[3].X = x;
2668     ptf[3].Y = y + height;
2669
2670     save_state = SaveDC(graphics->hdc);
2671     EndPath(graphics->hdc);
2672     SelectObject(graphics->hdc, brush->gdibrush);
2673     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2674
2675     transform_and_round_points(graphics, pti, ptf, 4);
2676
2677     Polygon(graphics->hdc, pti, 4);
2678
2679     RestoreDC(graphics->hdc, save_state);
2680
2681     return Ok;
2682 }
2683
2684 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2685     INT count)
2686 {
2687     GpStatus ret;
2688     INT i;
2689
2690     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2691
2692     if(!rects)
2693         return InvalidParameter;
2694
2695     for(i = 0; i < count; i++){
2696         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2697         if(ret != Ok)   return ret;
2698     }
2699
2700     return Ok;
2701 }
2702
2703 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2704     INT count)
2705 {
2706     GpRectF *rectsF;
2707     GpStatus ret;
2708     INT i;
2709
2710     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2711
2712     if(!rects || count <= 0)
2713         return InvalidParameter;
2714
2715     rectsF = GdipAlloc(sizeof(GpRectF)*count);
2716     if(!rectsF)
2717         return OutOfMemory;
2718
2719     for(i = 0; i < count; i++){
2720         rectsF[i].X      = (REAL)rects[i].X;
2721         rectsF[i].Y      = (REAL)rects[i].Y;
2722         rectsF[i].X      = (REAL)rects[i].Width;
2723         rectsF[i].Height = (REAL)rects[i].Height;
2724     }
2725
2726     ret = GdipFillRectangles(graphics,brush,rectsF,count);
2727     GdipFree(rectsF);
2728
2729     return ret;
2730 }
2731
2732 /*****************************************************************************
2733  * GdipFillRegion [GDIPLUS.@]
2734  */
2735 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2736         GpRegion* region)
2737 {
2738     INT save_state;
2739     GpStatus status;
2740     HRGN hrgn;
2741
2742     TRACE("(%p, %p, %p)\n", graphics, brush, region);
2743
2744     if (!(graphics && brush && region))
2745         return InvalidParameter;
2746
2747     if(graphics->busy)
2748         return ObjectBusy;
2749
2750     status = GdipGetRegionHRgn(region, graphics, &hrgn);
2751     if(status != Ok)
2752         return status;
2753
2754     save_state = SaveDC(graphics->hdc);
2755     EndPath(graphics->hdc);
2756     SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2757
2758     FillRgn(graphics->hdc, hrgn, brush->gdibrush);
2759
2760     RestoreDC(graphics->hdc, save_state);
2761
2762     DeleteObject(hrgn);
2763
2764     return Ok;
2765 }
2766
2767 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2768 {
2769     static int calls;
2770
2771     if(!graphics)
2772         return InvalidParameter;
2773
2774     if(graphics->busy)
2775         return ObjectBusy;
2776
2777     if(!(calls++))
2778         FIXME("not implemented\n");
2779
2780     return NotImplemented;
2781 }
2782
2783 /*****************************************************************************
2784  * GdipGetClipBounds [GDIPLUS.@]
2785  */
2786 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
2787 {
2788     TRACE("(%p, %p)\n", graphics, rect);
2789
2790     if(!graphics)
2791         return InvalidParameter;
2792
2793     if(graphics->busy)
2794         return ObjectBusy;
2795
2796     return GdipGetRegionBounds(graphics->clip, graphics, rect);
2797 }
2798
2799 /*****************************************************************************
2800  * GdipGetClipBoundsI [GDIPLUS.@]
2801  */
2802 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
2803 {
2804     TRACE("(%p, %p)\n", graphics, rect);
2805
2806     if(!graphics)
2807         return InvalidParameter;
2808
2809     if(graphics->busy)
2810         return ObjectBusy;
2811
2812     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
2813 }
2814
2815 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2816 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2817     CompositingMode *mode)
2818 {
2819     TRACE("(%p, %p)\n", graphics, mode);
2820
2821     if(!graphics || !mode)
2822         return InvalidParameter;
2823
2824     if(graphics->busy)
2825         return ObjectBusy;
2826
2827     *mode = graphics->compmode;
2828
2829     return Ok;
2830 }
2831
2832 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2833 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2834     CompositingQuality *quality)
2835 {
2836     TRACE("(%p, %p)\n", graphics, quality);
2837
2838     if(!graphics || !quality)
2839         return InvalidParameter;
2840
2841     if(graphics->busy)
2842         return ObjectBusy;
2843
2844     *quality = graphics->compqual;
2845
2846     return Ok;
2847 }
2848
2849 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2850 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2851     InterpolationMode *mode)
2852 {
2853     TRACE("(%p, %p)\n", graphics, mode);
2854
2855     if(!graphics || !mode)
2856         return InvalidParameter;
2857
2858     if(graphics->busy)
2859         return ObjectBusy;
2860
2861     *mode = graphics->interpolation;
2862
2863     return Ok;
2864 }
2865
2866 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2867 {
2868     if(!graphics || !argb)
2869         return InvalidParameter;
2870
2871     if(graphics->busy)
2872         return ObjectBusy;
2873
2874     FIXME("(%p, %p): stub\n", graphics, argb);
2875
2876     return NotImplemented;
2877 }
2878
2879 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2880 {
2881     TRACE("(%p, %p)\n", graphics, scale);
2882
2883     if(!graphics || !scale)
2884         return InvalidParameter;
2885
2886     if(graphics->busy)
2887         return ObjectBusy;
2888
2889     *scale = graphics->scale;
2890
2891     return Ok;
2892 }
2893
2894 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2895 {
2896     TRACE("(%p, %p)\n", graphics, unit);
2897
2898     if(!graphics || !unit)
2899         return InvalidParameter;
2900
2901     if(graphics->busy)
2902         return ObjectBusy;
2903
2904     *unit = graphics->unit;
2905
2906     return Ok;
2907 }
2908
2909 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2910 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2911     *mode)
2912 {
2913     TRACE("(%p, %p)\n", graphics, mode);
2914
2915     if(!graphics || !mode)
2916         return InvalidParameter;
2917
2918     if(graphics->busy)
2919         return ObjectBusy;
2920
2921     *mode = graphics->pixeloffset;
2922
2923     return Ok;
2924 }
2925
2926 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2927 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2928 {
2929     TRACE("(%p, %p)\n", graphics, mode);
2930
2931     if(!graphics || !mode)
2932         return InvalidParameter;
2933
2934     if(graphics->busy)
2935         return ObjectBusy;
2936
2937     *mode = graphics->smoothing;
2938
2939     return Ok;
2940 }
2941
2942 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2943 {
2944     TRACE("(%p, %p)\n", graphics, contrast);
2945
2946     if(!graphics || !contrast)
2947         return InvalidParameter;
2948
2949     *contrast = graphics->textcontrast;
2950
2951     return Ok;
2952 }
2953
2954 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2955 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2956     TextRenderingHint *hint)
2957 {
2958     TRACE("(%p, %p)\n", graphics, hint);
2959
2960     if(!graphics || !hint)
2961         return InvalidParameter;
2962
2963     if(graphics->busy)
2964         return ObjectBusy;
2965
2966     *hint = graphics->texthint;
2967
2968     return Ok;
2969 }
2970
2971 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2972 {
2973     TRACE("(%p, %p)\n", graphics, matrix);
2974
2975     if(!graphics || !matrix)
2976         return InvalidParameter;
2977
2978     if(graphics->busy)
2979         return ObjectBusy;
2980
2981     *matrix = *graphics->worldtrans;
2982     return Ok;
2983 }
2984
2985 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2986 {
2987     GpSolidFill *brush;
2988     GpStatus stat;
2989     RECT rect;
2990
2991     TRACE("(%p, %x)\n", graphics, color);
2992
2993     if(!graphics)
2994         return InvalidParameter;
2995
2996     if(graphics->busy)
2997         return ObjectBusy;
2998
2999     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3000         return stat;
3001
3002     if(graphics->hwnd){
3003         if(!GetWindowRect(graphics->hwnd, &rect)){
3004             GdipDeleteBrush((GpBrush*)brush);
3005             return GenericError;
3006         }
3007
3008         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right  - rect.left),
3009                                                                (REAL)(rect.bottom - rect.top));
3010     }
3011     else
3012         GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
3013                                                                (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
3014
3015     GdipDeleteBrush((GpBrush*)brush);
3016
3017     return Ok;
3018 }
3019
3020 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3021 {
3022     TRACE("(%p, %p)\n", graphics, res);
3023
3024     if(!graphics || !res)
3025         return InvalidParameter;
3026
3027     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3028 }
3029
3030 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3031 {
3032     FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
3033
3034     if(!graphics || !result)
3035         return InvalidParameter;
3036
3037     if(graphics->busy)
3038         return ObjectBusy;
3039
3040     return NotImplemented;
3041 }
3042
3043 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3044 {
3045     FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
3046
3047     if(!graphics || !result)
3048         return InvalidParameter;
3049
3050     if(graphics->busy)
3051         return ObjectBusy;
3052
3053     return NotImplemented;
3054 }
3055
3056 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
3057         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
3058         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
3059         INT regionCount, GpRegion** regions)
3060 {
3061     if (!(graphics && string && font && layoutRect && stringFormat && regions))
3062         return InvalidParameter;
3063
3064     FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
3065             length, font, layoutRect, stringFormat, regionCount, regions);
3066
3067     return NotImplemented;
3068 }
3069
3070 /* Find the smallest rectangle that bounds the text when it is printed in rect
3071  * according to the format options listed in format. If rect has 0 width and
3072  * height, then just find the smallest rectangle that bounds the text when it's
3073  * printed at location (rect->X, rect-Y). */
3074 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
3075     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3076     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
3077     INT *codepointsfitted, INT *linesfilled)
3078 {
3079     HFONT oldfont;
3080     WCHAR* stringdup;
3081     INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
3082         nheight;
3083     SIZE size;
3084
3085     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
3086         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
3087         bounds, codepointsfitted, linesfilled);
3088
3089     if(!graphics || !string || !font || !rect)
3090         return InvalidParameter;
3091
3092     if(linesfilled) *linesfilled = 0;
3093     if(codepointsfitted) *codepointsfitted = 0;
3094
3095     if(format)
3096         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3097
3098     if(length == -1) length = lstrlenW(string);
3099
3100     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
3101     if(!stringdup) return OutOfMemory;
3102
3103     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3104     nwidth = roundr(rect->Width);
3105     nheight = roundr(rect->Height);
3106
3107     if((nwidth == 0) && (nheight == 0))
3108         nwidth = nheight = INT_MAX;
3109
3110     for(i = 0, j = 0; i < length; i++){
3111         if(!isprintW(string[i]) && (string[i] != '\n'))
3112             continue;
3113
3114         stringdup[j] = string[i];
3115         j++;
3116     }
3117
3118     stringdup[j] = 0;
3119     length = j;
3120
3121     while(sum < length){
3122         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
3123                               nwidth, &fit, NULL, &size);
3124         fitcpy = fit;
3125
3126         if(fit == 0)
3127             break;
3128
3129         for(lret = 0; lret < fit; lret++)
3130             if(*(stringdup + sum + lret) == '\n')
3131                 break;
3132
3133         /* Line break code (may look strange, but it imitates windows). */
3134         if(lret < fit)
3135             fit = lret;    /* this is not an off-by-one error */
3136         else if(fit < (length - sum)){
3137             if(*(stringdup + sum + fit) == ' ')
3138                 while(*(stringdup + sum + fit) == ' ')
3139                     fit++;
3140             else
3141                 while(*(stringdup + sum + fit - 1) != ' '){
3142                     fit--;
3143
3144                     if(*(stringdup + sum + fit) == '\t')
3145                         break;
3146
3147                     if(fit == 0){
3148                         fit = fitcpy;
3149                         break;
3150                     }
3151                 }
3152         }
3153
3154         GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
3155                               nwidth, &j, NULL, &size);
3156
3157         sum += fit + (lret < fitcpy ? 1 : 0);
3158         if(codepointsfitted) *codepointsfitted = sum;
3159
3160         height += size.cy;
3161         if(linesfilled) *linesfilled += size.cy;
3162         max_width = max(max_width, size.cx);
3163
3164         if(height > nheight)
3165             break;
3166
3167         /* Stop if this was a linewrap (but not if it was a linebreak). */
3168         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3169             break;
3170     }
3171
3172     bounds->X = rect->X;
3173     bounds->Y = rect->Y;
3174     bounds->Width = (REAL)max_width;
3175     bounds->Height = (REAL) min(height, nheight);
3176
3177     GdipFree(stringdup);
3178     DeleteObject(SelectObject(graphics->hdc, oldfont));
3179
3180     return Ok;
3181 }
3182
3183 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3184 {
3185     TRACE("(%p)\n", graphics);
3186
3187     if(!graphics)
3188         return InvalidParameter;
3189
3190     if(graphics->busy)
3191         return ObjectBusy;
3192
3193     return GdipSetInfinite(graphics->clip);
3194 }
3195
3196 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3197 {
3198     TRACE("(%p)\n", graphics);
3199
3200     if(!graphics)
3201         return InvalidParameter;
3202
3203     if(graphics->busy)
3204         return ObjectBusy;
3205
3206     graphics->worldtrans->matrix[0] = 1.0;
3207     graphics->worldtrans->matrix[1] = 0.0;
3208     graphics->worldtrans->matrix[2] = 0.0;
3209     graphics->worldtrans->matrix[3] = 1.0;
3210     graphics->worldtrans->matrix[4] = 0.0;
3211     graphics->worldtrans->matrix[5] = 0.0;
3212
3213     return Ok;
3214 }
3215
3216 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3217 {
3218     static int calls;
3219
3220     if(!graphics)
3221         return InvalidParameter;
3222
3223     if(!(calls++))
3224         FIXME("graphics state not implemented\n");
3225
3226     return Ok;
3227 }
3228
3229 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3230     GpMatrixOrder order)
3231 {
3232     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3233
3234     if(!graphics)
3235         return InvalidParameter;
3236
3237     if(graphics->busy)
3238         return ObjectBusy;
3239
3240     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3241 }
3242
3243 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3244 {
3245     static int calls;
3246
3247     if(!graphics || !state)
3248         return InvalidParameter;
3249
3250     if(!(calls++))
3251         FIXME("graphics state not implemented\n");
3252
3253     *state = 0xdeadbeef;
3254     return Ok;
3255 }
3256
3257 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
3258 {
3259     FIXME("(%p, %p)\n", graphics, state);
3260
3261     if(!graphics || !state)
3262         return InvalidParameter;
3263
3264     *state = 0xdeadbeef;
3265     return Ok;
3266 }
3267
3268 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
3269 {
3270     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
3271     return NotImplemented;
3272 }
3273
3274 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
3275 {
3276     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
3277     return NotImplemented;
3278 }
3279
3280 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
3281 {
3282     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
3283     return NotImplemented;
3284 }
3285
3286 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
3287 {
3288     FIXME("(%p, 0x%x)\n", graphics, state);
3289
3290     if(!graphics || !state)
3291         return InvalidParameter;
3292
3293     return Ok;
3294 }
3295
3296 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
3297     REAL sy, GpMatrixOrder order)
3298 {
3299     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
3300
3301     if(!graphics)
3302         return InvalidParameter;
3303
3304     if(graphics->busy)
3305         return ObjectBusy;
3306
3307     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
3308 }
3309
3310 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
3311     CombineMode mode)
3312 {
3313     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
3314
3315     if(!graphics || !srcgraphics)
3316         return InvalidParameter;
3317
3318     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
3319 }
3320
3321 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
3322     CompositingMode mode)
3323 {
3324     TRACE("(%p, %d)\n", graphics, mode);
3325
3326     if(!graphics)
3327         return InvalidParameter;
3328
3329     if(graphics->busy)
3330         return ObjectBusy;
3331
3332     graphics->compmode = mode;
3333
3334     return Ok;
3335 }
3336
3337 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
3338     CompositingQuality quality)
3339 {
3340     TRACE("(%p, %d)\n", graphics, quality);
3341
3342     if(!graphics)
3343         return InvalidParameter;
3344
3345     if(graphics->busy)
3346         return ObjectBusy;
3347
3348     graphics->compqual = quality;
3349
3350     return Ok;
3351 }
3352
3353 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
3354     InterpolationMode mode)
3355 {
3356     TRACE("(%p, %d)\n", graphics, mode);
3357
3358     if(!graphics)
3359         return InvalidParameter;
3360
3361     if(graphics->busy)
3362         return ObjectBusy;
3363
3364     graphics->interpolation = mode;
3365
3366     return Ok;
3367 }
3368
3369 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
3370 {
3371     TRACE("(%p, %.2f)\n", graphics, scale);
3372
3373     if(!graphics || (scale <= 0.0))
3374         return InvalidParameter;
3375
3376     if(graphics->busy)
3377         return ObjectBusy;
3378
3379     graphics->scale = scale;
3380
3381     return Ok;
3382 }
3383
3384 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
3385 {
3386     TRACE("(%p, %d)\n", graphics, unit);
3387
3388     if(!graphics)
3389         return InvalidParameter;
3390
3391     if(graphics->busy)
3392         return ObjectBusy;
3393
3394     if(unit == UnitWorld)
3395         return InvalidParameter;
3396
3397     graphics->unit = unit;
3398
3399     return Ok;
3400 }
3401
3402 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3403     mode)
3404 {
3405     TRACE("(%p, %d)\n", graphics, mode);
3406
3407     if(!graphics)
3408         return InvalidParameter;
3409
3410     if(graphics->busy)
3411         return ObjectBusy;
3412
3413     graphics->pixeloffset = mode;
3414
3415     return Ok;
3416 }
3417
3418 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
3419 {
3420     static int calls;
3421
3422     TRACE("(%p,%i,%i)\n", graphics, x, y);
3423
3424     if (!(calls++))
3425         FIXME("not implemented\n");
3426
3427     return NotImplemented;
3428 }
3429
3430 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
3431 {
3432     TRACE("(%p, %d)\n", graphics, mode);
3433
3434     if(!graphics)
3435         return InvalidParameter;
3436
3437     if(graphics->busy)
3438         return ObjectBusy;
3439
3440     graphics->smoothing = mode;
3441
3442     return Ok;
3443 }
3444
3445 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3446 {
3447     TRACE("(%p, %d)\n", graphics, contrast);
3448
3449     if(!graphics)
3450         return InvalidParameter;
3451
3452     graphics->textcontrast = contrast;
3453
3454     return Ok;
3455 }
3456
3457 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3458     TextRenderingHint hint)
3459 {
3460     TRACE("(%p, %d)\n", graphics, hint);
3461
3462     if(!graphics)
3463         return InvalidParameter;
3464
3465     if(graphics->busy)
3466         return ObjectBusy;
3467
3468     graphics->texthint = hint;
3469
3470     return Ok;
3471 }
3472
3473 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3474 {
3475     TRACE("(%p, %p)\n", graphics, matrix);
3476
3477     if(!graphics || !matrix)
3478         return InvalidParameter;
3479
3480     if(graphics->busy)
3481         return ObjectBusy;
3482
3483     GdipDeleteMatrix(graphics->worldtrans);
3484     return GdipCloneMatrix(matrix, &graphics->worldtrans);
3485 }
3486
3487 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3488     REAL dy, GpMatrixOrder order)
3489 {
3490     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3491
3492     if(!graphics)
3493         return InvalidParameter;
3494
3495     if(graphics->busy)
3496         return ObjectBusy;
3497
3498     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3499 }
3500
3501 /*****************************************************************************
3502  * GdipSetClipHrgn [GDIPLUS.@]
3503  */
3504 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
3505 {
3506     GpRegion *region;
3507     GpStatus status;
3508
3509     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
3510
3511     if(!graphics)
3512         return InvalidParameter;
3513
3514     status = GdipCreateRegionHrgn(hrgn, &region);
3515     if(status != Ok)
3516         return status;
3517
3518     status = GdipSetClipRegion(graphics, region, mode);
3519
3520     GdipDeleteRegion(region);
3521     return status;
3522 }
3523
3524 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3525 {
3526     TRACE("(%p, %p, %d)\n", graphics, path, mode);
3527
3528     if(!graphics)
3529         return InvalidParameter;
3530
3531     if(graphics->busy)
3532         return ObjectBusy;
3533
3534     return GdipCombineRegionPath(graphics->clip, path, mode);
3535 }
3536
3537 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3538                                     REAL width, REAL height,
3539                                     CombineMode mode)
3540 {
3541     GpRectF rect;
3542
3543     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3544
3545     if(!graphics)
3546         return InvalidParameter;
3547
3548     if(graphics->busy)
3549         return ObjectBusy;
3550
3551     rect.X = x;
3552     rect.Y = y;
3553     rect.Width  = width;
3554     rect.Height = height;
3555
3556     return GdipCombineRegionRect(graphics->clip, &rect, mode);
3557 }
3558
3559 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3560                                      INT width, INT height,
3561                                      CombineMode mode)
3562 {
3563     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3564
3565     if(!graphics)
3566         return InvalidParameter;
3567
3568     if(graphics->busy)
3569         return ObjectBusy;
3570
3571     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3572 }
3573
3574 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3575                                       CombineMode mode)
3576 {
3577     TRACE("(%p, %p, %d)\n", graphics, region, mode);
3578
3579     if(!graphics || !region)
3580         return InvalidParameter;
3581
3582     if(graphics->busy)
3583         return ObjectBusy;
3584
3585     return GdipCombineRegionRegion(graphics->clip, region, mode);
3586 }
3587
3588 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3589     UINT limitDpi)
3590 {
3591     static int calls;
3592
3593     if(!(calls++))
3594         FIXME("not implemented\n");
3595
3596     return NotImplemented;
3597 }
3598
3599 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3600     INT count)
3601 {
3602     INT save_state;
3603     POINT *pti;
3604
3605     TRACE("(%p, %p, %d)\n", graphics, points, count);
3606
3607     if(!graphics || !pen || count<=0)
3608         return InvalidParameter;
3609
3610     if(graphics->busy)
3611         return ObjectBusy;
3612
3613     pti = GdipAlloc(sizeof(POINT) * count);
3614
3615     save_state = prepare_dc(graphics, pen);
3616     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3617
3618     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3619     Polygon(graphics->hdc, pti, count);
3620
3621     restore_dc(graphics, save_state);
3622     GdipFree(pti);
3623
3624     return Ok;
3625 }
3626
3627 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3628     INT count)
3629 {
3630     GpStatus ret;
3631     GpPointF *ptf;
3632     INT i;
3633
3634     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3635
3636     if(count<=0)    return InvalidParameter;
3637     ptf = GdipAlloc(sizeof(GpPointF) * count);
3638
3639     for(i = 0;i < count; i++){
3640         ptf[i].X = (REAL)points[i].X;
3641         ptf[i].Y = (REAL)points[i].Y;
3642     }
3643
3644     ret = GdipDrawPolygon(graphics,pen,ptf,count);
3645     GdipFree(ptf);
3646
3647     return ret;
3648 }
3649
3650 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3651 {
3652     TRACE("(%p, %p)\n", graphics, dpi);
3653
3654     if(!graphics || !dpi)
3655         return InvalidParameter;
3656
3657     if(graphics->busy)
3658         return ObjectBusy;
3659
3660     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3661
3662     return Ok;
3663 }
3664
3665 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3666 {
3667     TRACE("(%p, %p)\n", graphics, dpi);
3668
3669     if(!graphics || !dpi)
3670         return InvalidParameter;
3671
3672     if(graphics->busy)
3673         return ObjectBusy;
3674
3675     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3676
3677     return Ok;
3678 }
3679
3680 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3681     GpMatrixOrder order)
3682 {
3683     GpMatrix m;
3684     GpStatus ret;
3685
3686     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3687
3688     if(!graphics || !matrix)
3689         return InvalidParameter;
3690
3691     if(graphics->busy)
3692         return ObjectBusy;
3693
3694     m = *(graphics->worldtrans);
3695
3696     ret = GdipMultiplyMatrix(&m, matrix, order);
3697     if(ret == Ok)
3698         *(graphics->worldtrans) = m;
3699
3700     return ret;
3701 }
3702
3703 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3704 {
3705     TRACE("(%p, %p)\n", graphics, hdc);
3706
3707     if(!graphics || !hdc)
3708         return InvalidParameter;
3709
3710     if(graphics->busy)
3711         return ObjectBusy;
3712
3713     *hdc = graphics->hdc;
3714     graphics->busy = TRUE;
3715
3716     return Ok;
3717 }
3718
3719 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3720 {
3721     TRACE("(%p, %p)\n", graphics, hdc);
3722
3723     if(!graphics)
3724         return InvalidParameter;
3725
3726     if(graphics->hdc != hdc || !(graphics->busy))
3727         return InvalidParameter;
3728
3729     graphics->busy = FALSE;
3730
3731     return Ok;
3732 }
3733
3734 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3735 {
3736     GpRegion *clip;
3737     GpStatus status;
3738
3739     TRACE("(%p, %p)\n", graphics, region);
3740
3741     if(!graphics || !region)
3742         return InvalidParameter;
3743
3744     if(graphics->busy)
3745         return ObjectBusy;
3746
3747     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3748         return status;
3749
3750     /* free everything except root node and header */
3751     delete_element(&region->node);
3752     memcpy(region, clip, sizeof(GpRegion));
3753
3754     return Ok;
3755 }
3756
3757 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3758                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
3759 {
3760     GpMatrix *matrix;
3761     GpStatus stat;
3762     REAL unitscale;
3763
3764     if(!graphics || !points || count <= 0)
3765         return InvalidParameter;
3766
3767     if(graphics->busy)
3768         return ObjectBusy;
3769
3770     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
3771
3772     if (src_space == dst_space) return Ok;
3773
3774     stat = GdipCreateMatrix(&matrix);
3775     if (stat == Ok)
3776     {
3777         unitscale = convert_unit(graphics->hdc, graphics->unit);
3778
3779         if(graphics->unit != UnitDisplay)
3780             unitscale *= graphics->scale;
3781
3782         /* transform from src_space to CoordinateSpacePage */
3783         switch (src_space)
3784         {
3785         case CoordinateSpaceWorld:
3786             GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
3787             break;
3788         case CoordinateSpacePage:
3789             break;
3790         case CoordinateSpaceDevice:
3791             GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
3792             break;
3793         }
3794
3795         /* transform from CoordinateSpacePage to dst_space */
3796         switch (dst_space)
3797         {
3798         case CoordinateSpaceWorld:
3799             {
3800                 GpMatrix *inverted_transform;
3801                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
3802                 if (stat == Ok)
3803                 {
3804                     stat = GdipInvertMatrix(inverted_transform);
3805                     if (stat == Ok)
3806                         GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
3807                     GdipDeleteMatrix(inverted_transform);
3808                 }
3809                 break;
3810             }
3811         case CoordinateSpacePage:
3812             break;
3813         case CoordinateSpaceDevice:
3814             GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
3815             break;
3816         }
3817
3818         if (stat == Ok)
3819             stat = GdipTransformMatrixPoints(matrix, points, count);
3820
3821         GdipDeleteMatrix(matrix);
3822     }
3823
3824     return stat;
3825 }
3826
3827 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3828                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
3829 {
3830     GpPointF *pointsF;
3831     GpStatus ret;
3832     INT i;
3833
3834     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
3835
3836     if(count <= 0)
3837         return InvalidParameter;
3838
3839     pointsF = GdipAlloc(sizeof(GpPointF) * count);
3840     if(!pointsF)
3841         return OutOfMemory;
3842
3843     for(i = 0; i < count; i++){
3844         pointsF[i].X = (REAL)points[i].X;
3845         pointsF[i].Y = (REAL)points[i].Y;
3846     }
3847
3848     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
3849
3850     if(ret == Ok)
3851         for(i = 0; i < count; i++){
3852             points[i].X = roundr(pointsF[i].X);
3853             points[i].Y = roundr(pointsF[i].Y);
3854         }
3855     GdipFree(pointsF);
3856
3857     return ret;
3858 }
3859
3860 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3861 {
3862     FIXME("\n");
3863
3864     return NULL;
3865 }
3866
3867 /*****************************************************************************
3868  * GdipTranslateClip [GDIPLUS.@]
3869  */
3870 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
3871 {
3872     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
3873
3874     if(!graphics)
3875         return InvalidParameter;
3876
3877     if(graphics->busy)
3878         return ObjectBusy;
3879
3880     return GdipTranslateRegion(graphics->clip, dx, dy);
3881 }
3882
3883 /*****************************************************************************
3884  * GdipTranslateClipI [GDIPLUS.@]
3885  */
3886 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
3887 {
3888     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
3889
3890     if(!graphics)
3891         return InvalidParameter;
3892
3893     if(graphics->busy)
3894         return ObjectBusy;
3895
3896     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
3897 }
3898
3899
3900 /*****************************************************************************
3901  * GdipMeasureDriverString [GDIPLUS.@]
3902  */
3903 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
3904                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
3905                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
3906 {
3907     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
3908     return NotImplemented;
3909 }
3910
3911 /*****************************************************************************
3912  * GdipGetVisibleClipBoundsI [GDIPLUS.@]
3913  */
3914 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3915 {
3916     FIXME("(%p %p): stub\n", graphics, rect);
3917     return NotImplemented;
3918 }
3919
3920 /*****************************************************************************
3921  * GdipDrawDriverString [GDIPLUS.@]
3922  */
3923 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
3924                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
3925                                          GDIPCONST PointF *positions, INT flags,
3926                                          GDIPCONST GpMatrix *matrix )
3927 {
3928     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
3929     return NotImplemented;
3930 }
3931
3932 /*****************************************************************************
3933  * GdipIsVisibleRegionPointI [GDIPLUS.@]
3934  */
3935 GpStatus WINGDIPAPI GdipIsVisibleRegionPointI(GpRegion *region, INT x, INT y, GpGraphics *graphics, BOOL *result)
3936 {
3937     FIXME("(%p %d %d %p %p): stub\n", region, x, y, graphics, result);
3938     return NotImplemented;
3939 }