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