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