gdiplus: Fixed GdipFillClosedCurve2 and GdipFillClosedCurve2I in the cases that count...
[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 #include "wine/list.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
44
45 /* looks-right constants */
46 #define ANCHOR_WIDTH (2.0)
47 #define MAX_ITERS (50)
48
49 /* Converts angle (in degrees) to x/y coordinates */
50 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
51 {
52     REAL radAngle, hypotenuse;
53
54     radAngle = deg2rad(angle);
55     hypotenuse = 50.0; /* arbitrary */
56
57     *x = x_0 + cos(radAngle) * hypotenuse;
58     *y = y_0 + sin(radAngle) * hypotenuse;
59 }
60
61 /* Converts from gdiplus path point type to gdi path point type. */
62 static BYTE convert_path_point_type(BYTE type)
63 {
64     BYTE ret;
65
66     switch(type & PathPointTypePathTypeMask){
67         case PathPointTypeBezier:
68             ret = PT_BEZIERTO;
69             break;
70         case PathPointTypeLine:
71             ret = PT_LINETO;
72             break;
73         case PathPointTypeStart:
74             ret = PT_MOVETO;
75             break;
76         default:
77             ERR("Bad point type\n");
78             return 0;
79     }
80
81     if(type & PathPointTypeCloseSubpath)
82         ret |= PT_CLOSEFIGURE;
83
84     return ret;
85 }
86
87 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
88 {
89     HPEN gdipen;
90     REAL width;
91     INT save_state = SaveDC(graphics->hdc), i, numdashes;
92     GpPointF pt[2];
93     DWORD dash_array[MAX_DASHLEN];
94
95     EndPath(graphics->hdc);
96
97     if(pen->unit == UnitPixel){
98         width = pen->width;
99     }
100     else{
101         /* Get an estimate for the amount the pen width is affected by the world
102          * transform. (This is similar to what some of the wine drivers do.) */
103         pt[0].X = 0.0;
104         pt[0].Y = 0.0;
105         pt[1].X = 1.0;
106         pt[1].Y = 1.0;
107         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
108         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
109                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
110
111         width *= pen->width * convert_unit(graphics->hdc,
112                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
113     }
114
115     if(pen->dash == DashStyleCustom){
116         numdashes = min(pen->numdashes, MAX_DASHLEN);
117
118         TRACE("dashes are: ");
119         for(i = 0; i < numdashes; i++){
120             dash_array[i] = roundr(width * pen->dashes[i]);
121             TRACE("%d, ", dash_array[i]);
122         }
123         TRACE("\n and the pen style is %x\n", pen->style);
124
125         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
126                               numdashes, dash_array);
127     }
128     else
129         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
130
131     SelectObject(graphics->hdc, gdipen);
132
133     return save_state;
134 }
135
136 static void restore_dc(GpGraphics *graphics, INT state)
137 {
138     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
139     RestoreDC(graphics->hdc, state);
140 }
141
142 /* This helper applies all the changes that the points listed in ptf need in
143  * order to be drawn on the device context.  In the end, this should include at
144  * least:
145  *  -scaling by page unit
146  *  -applying world transformation
147  *  -converting from float to int
148  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
149  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
150  * gdi to draw, and these functions would irreparably mess with line widths.
151  */
152 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
153     GpPointF *ptf, INT count)
154 {
155     REAL unitscale;
156     GpMatrix *matrix;
157     int i;
158
159     unitscale = convert_unit(graphics->hdc, graphics->unit);
160
161     /* apply page scale */
162     if(graphics->unit != UnitDisplay)
163         unitscale *= graphics->scale;
164
165     GdipCloneMatrix(graphics->worldtrans, &matrix);
166     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
167     GdipTransformMatrixPoints(matrix, ptf, count);
168     GdipDeleteMatrix(matrix);
169
170     for(i = 0; i < count; i++){
171         pti[i].x = roundr(ptf[i].X);
172         pti[i].y = roundr(ptf[i].Y);
173     }
174 }
175
176 /* Draw non-premultiplied ARGB data to the given graphics object */
177 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
178     const BYTE *src, INT src_width, INT src_height, INT src_stride)
179 {
180     if (graphics->image && graphics->image->type == ImageTypeBitmap)
181     {
182         GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
183         INT x, y;
184
185         for (x=0; x<src_width; x++)
186         {
187             for (y=0; y<src_height; y++)
188             {
189                 ARGB dst_color, src_color;
190                 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
191                 src_color = ((ARGB*)(src + src_stride * y))[x];
192                 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
193             }
194         }
195
196         return Ok;
197     }
198     else
199     {
200         HDC hdc;
201         HBITMAP hbitmap, old_hbm=NULL;
202         BITMAPINFOHEADER bih;
203         BYTE *temp_bits;
204         BLENDFUNCTION bf;
205
206         hdc = CreateCompatibleDC(0);
207
208         bih.biSize = sizeof(BITMAPINFOHEADER);
209         bih.biWidth = src_width;
210         bih.biHeight = -src_height;
211         bih.biPlanes = 1;
212         bih.biBitCount = 32;
213         bih.biCompression = BI_RGB;
214         bih.biSizeImage = 0;
215         bih.biXPelsPerMeter = 0;
216         bih.biYPelsPerMeter = 0;
217         bih.biClrUsed = 0;
218         bih.biClrImportant = 0;
219
220         hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
221             (void**)&temp_bits, NULL, 0);
222
223         convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
224             4 * src_width, src, src_stride);
225
226         old_hbm = SelectObject(hdc, hbitmap);
227
228         bf.BlendOp = AC_SRC_OVER;
229         bf.BlendFlags = 0;
230         bf.SourceConstantAlpha = 255;
231         bf.AlphaFormat = AC_SRC_ALPHA;
232
233         GdiAlphaBlend(graphics->hdc, dst_x, dst_y, src_width, src_height,
234             hdc, 0, 0, src_width, src_height, bf);
235
236         SelectObject(hdc, old_hbm);
237         DeleteDC(hdc);
238         DeleteObject(hbitmap);
239
240         return Ok;
241     }
242 }
243
244 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
245 {
246     ARGB result=0;
247     ARGB i;
248     for (i=0xff; i<=0xff0000; i = i << 8)
249         result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
250     return result;
251 }
252
253 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
254 {
255     REAL blendfac;
256
257     /* clamp to between 0.0 and 1.0, using the wrap mode */
258     if (brush->wrap == WrapModeTile)
259     {
260         position = fmodf(position, 1.0f);
261         if (position < 0.0f) position += 1.0f;
262     }
263     else /* WrapModeFlip* */
264     {
265         position = fmodf(position, 2.0f);
266         if (position < 0.0f) position += 2.0f;
267         if (position > 1.0f) position = 2.0f - position;
268     }
269
270     if (brush->blendcount == 1)
271         blendfac = position;
272     else
273     {
274         int i=1;
275         REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
276         REAL range;
277
278         /* locate the blend positions surrounding this position */
279         while (position > brush->blendpos[i])
280             i++;
281
282         /* interpolate between the blend positions */
283         left_blendpos = brush->blendpos[i-1];
284         left_blendfac = brush->blendfac[i-1];
285         right_blendpos = brush->blendpos[i];
286         right_blendfac = brush->blendfac[i];
287         range = right_blendpos - left_blendpos;
288         blendfac = (left_blendfac * (right_blendpos - position) +
289                     right_blendfac * (position - left_blendpos)) / range;
290     }
291
292     if (brush->pblendcount == 0)
293         return blend_colors(brush->startcolor, brush->endcolor, blendfac);
294     else
295     {
296         int i=1;
297         ARGB left_blendcolor, right_blendcolor;
298         REAL left_blendpos, right_blendpos;
299
300         /* locate the blend colors surrounding this position */
301         while (blendfac > brush->pblendpos[i])
302             i++;
303
304         /* interpolate between the blend colors */
305         left_blendpos = brush->pblendpos[i-1];
306         left_blendcolor = brush->pblendcolor[i-1];
307         right_blendpos = brush->pblendpos[i];
308         right_blendcolor = brush->pblendcolor[i];
309         blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
310         return blend_colors(left_blendcolor, right_blendcolor, blendfac);
311     }
312 }
313
314 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
315 {
316     switch (brush->bt)
317     {
318     case BrushTypeLinearGradient:
319     {
320         GpLineGradient *line = (GpLineGradient*)brush;
321         RECT rc;
322
323         SelectClipPath(graphics->hdc, RGN_AND);
324         if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
325         {
326             GpPointF endpointsf[2];
327             POINT endpointsi[2];
328             POINT poly[4];
329
330             SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
331
332             endpointsf[0] = line->startpoint;
333             endpointsf[1] = line->endpoint;
334             transform_and_round_points(graphics, endpointsi, endpointsf, 2);
335
336             if (abs(endpointsi[0].x-endpointsi[1].x) > abs(endpointsi[0].y-endpointsi[1].y))
337             {
338                 /* vertical-ish gradient */
339                 int startx, endx; /* x co-ordinates of endpoints shifted to intersect the top of the visible rectangle */
340                 int startbottomx; /* x co-ordinate of start point shifted to intersect the bottom of the visible rectangle */
341                 int width;
342                 COLORREF col;
343                 HBRUSH hbrush, hprevbrush;
344                 int leftx, rightx; /* x co-ordinates where the leftmost and rightmost gradient lines hit the top of the visible rectangle */
345                 int x;
346                 int tilt; /* horizontal distance covered by a gradient line */
347
348                 startx = roundr((rc.top - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
349                 endx = roundr((rc.top - endpointsf[1].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[1].X);
350                 width = endx - startx;
351                 startbottomx = roundr((rc.bottom - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
352                 tilt = startx - startbottomx;
353
354                 if (startx >= startbottomx)
355                 {
356                     leftx = rc.left;
357                     rightx = rc.right + tilt;
358                 }
359                 else
360                 {
361                     leftx = rc.left + tilt;
362                     rightx = rc.right;
363                 }
364
365                 poly[0].y = rc.bottom;
366                 poly[1].y = rc.top;
367                 poly[2].y = rc.top;
368                 poly[3].y = rc.bottom;
369
370                 for (x=leftx; x<=rightx; x++)
371                 {
372                     ARGB argb = blend_line_gradient(line, (x-startx)/(REAL)width);
373                     col = ARGB2COLORREF(argb);
374                     hbrush = CreateSolidBrush(col);
375                     hprevbrush = SelectObject(graphics->hdc, hbrush);
376                     poly[0].x = x - tilt - 1;
377                     poly[1].x = x - 1;
378                     poly[2].x = x;
379                     poly[3].x = x - tilt;
380                     Polygon(graphics->hdc, poly, 4);
381                     SelectObject(graphics->hdc, hprevbrush);
382                     DeleteObject(hbrush);
383                 }
384             }
385             else if (endpointsi[0].y != endpointsi[1].y)
386             {
387                 /* horizontal-ish gradient */
388                 int starty, endy; /* y co-ordinates of endpoints shifted to intersect the left of the visible rectangle */
389                 int startrighty; /* y co-ordinate of start point shifted to intersect the right of the visible rectangle */
390                 int height;
391                 COLORREF col;
392                 HBRUSH hbrush, hprevbrush;
393                 int topy, bottomy; /* y co-ordinates where the topmost and bottommost gradient lines hit the left of the visible rectangle */
394                 int y;
395                 int tilt; /* vertical distance covered by a gradient line */
396
397                 starty = roundr((rc.left - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
398                 endy = roundr((rc.left - endpointsf[1].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[1].Y);
399                 height = endy - starty;
400                 startrighty = roundr((rc.right - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
401                 tilt = starty - startrighty;
402
403                 if (starty >= startrighty)
404                 {
405                     topy = rc.top;
406                     bottomy = rc.bottom + tilt;
407                 }
408                 else
409                 {
410                     topy = rc.top + tilt;
411                     bottomy = rc.bottom;
412                 }
413
414                 poly[0].x = rc.right;
415                 poly[1].x = rc.left;
416                 poly[2].x = rc.left;
417                 poly[3].x = rc.right;
418
419                 for (y=topy; y<=bottomy; y++)
420                 {
421                     ARGB argb = blend_line_gradient(line, (y-starty)/(REAL)height);
422                     col = ARGB2COLORREF(argb);
423                     hbrush = CreateSolidBrush(col);
424                     hprevbrush = SelectObject(graphics->hdc, hbrush);
425                     poly[0].y = y - tilt - 1;
426                     poly[1].y = y - 1;
427                     poly[2].y = y;
428                     poly[3].y = y - tilt;
429                     Polygon(graphics->hdc, poly, 4);
430                     SelectObject(graphics->hdc, hprevbrush);
431                     DeleteObject(hbrush);
432                 }
433             }
434             /* else startpoint == endpoint */
435         }
436         break;
437     }
438     case BrushTypeSolidColor:
439     {
440         GpSolidFill *fill = (GpSolidFill*)brush;
441         if (fill->bmp)
442         {
443             RECT rc;
444             /* partially transparent fill */
445
446             SelectClipPath(graphics->hdc, RGN_AND);
447             if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
448             {
449                 HDC hdc = CreateCompatibleDC(NULL);
450                 HBITMAP oldbmp;
451                 BLENDFUNCTION bf;
452
453                 if (!hdc) break;
454
455                 oldbmp = SelectObject(hdc, fill->bmp);
456
457                 bf.BlendOp = AC_SRC_OVER;
458                 bf.BlendFlags = 0;
459                 bf.SourceConstantAlpha = 255;
460                 bf.AlphaFormat = AC_SRC_ALPHA;
461
462                 GdiAlphaBlend(graphics->hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, hdc, 0, 0, 1, 1, bf);
463
464                 SelectObject(hdc, oldbmp);
465                 DeleteDC(hdc);
466             }
467
468             break;
469         }
470         /* else fall through */
471     }
472     default:
473         SelectObject(graphics->hdc, brush->gdibrush);
474         FillPath(graphics->hdc);
475         break;
476     }
477 }
478
479 /* GdipDrawPie/GdipFillPie helper function */
480 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
481     REAL height, REAL startAngle, REAL sweepAngle)
482 {
483     GpPointF ptf[4];
484     POINT pti[4];
485
486     ptf[0].X = x;
487     ptf[0].Y = y;
488     ptf[1].X = x + width;
489     ptf[1].Y = y + height;
490
491     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
492     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
493
494     transform_and_round_points(graphics, pti, ptf, 4);
495
496     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
497         pti[2].y, pti[3].x, pti[3].y);
498 }
499
500 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
501  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
502  * should not be called on an hdc that has a path you care about. */
503 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
504     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
505 {
506     HGDIOBJ oldbrush = NULL, oldpen = NULL;
507     GpMatrix *matrix = NULL;
508     HBRUSH brush = NULL;
509     HPEN pen = NULL;
510     PointF ptf[4], *custptf = NULL;
511     POINT pt[4], *custpt = NULL;
512     BYTE *tp = NULL;
513     REAL theta, dsmall, dbig, dx, dy = 0.0;
514     INT i, count;
515     LOGBRUSH lb;
516     BOOL customstroke;
517
518     if((x1 == x2) && (y1 == y2))
519         return;
520
521     theta = gdiplus_atan2(y2 - y1, x2 - x1);
522
523     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
524     if(!customstroke){
525         brush = CreateSolidBrush(color);
526         lb.lbStyle = BS_SOLID;
527         lb.lbColor = color;
528         lb.lbHatch = 0;
529         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
530                            PS_JOIN_MITER, 1, &lb, 0,
531                            NULL);
532         oldbrush = SelectObject(graphics->hdc, brush);
533         oldpen = SelectObject(graphics->hdc, pen);
534     }
535
536     switch(cap){
537         case LineCapFlat:
538             break;
539         case LineCapSquare:
540         case LineCapSquareAnchor:
541         case LineCapDiamondAnchor:
542             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
543             if(cap == LineCapDiamondAnchor){
544                 dsmall = cos(theta + M_PI_2) * size;
545                 dbig = sin(theta + M_PI_2) * size;
546             }
547             else{
548                 dsmall = cos(theta + M_PI_4) * size;
549                 dbig = sin(theta + M_PI_4) * size;
550             }
551
552             ptf[0].X = x2 - dsmall;
553             ptf[1].X = x2 + dbig;
554
555             ptf[0].Y = y2 - dbig;
556             ptf[3].Y = y2 + dsmall;
557
558             ptf[1].Y = y2 - dsmall;
559             ptf[2].Y = y2 + dbig;
560
561             ptf[3].X = x2 - dbig;
562             ptf[2].X = x2 + dsmall;
563
564             transform_and_round_points(graphics, pt, ptf, 4);
565             Polygon(graphics->hdc, pt, 4);
566
567             break;
568         case LineCapArrowAnchor:
569             size = size * 4.0 / sqrt(3.0);
570
571             dx = cos(M_PI / 6.0 + theta) * size;
572             dy = sin(M_PI / 6.0 + theta) * size;
573
574             ptf[0].X = x2 - dx;
575             ptf[0].Y = y2 - dy;
576
577             dx = cos(- M_PI / 6.0 + theta) * size;
578             dy = sin(- M_PI / 6.0 + theta) * size;
579
580             ptf[1].X = x2 - dx;
581             ptf[1].Y = y2 - dy;
582
583             ptf[2].X = x2;
584             ptf[2].Y = y2;
585
586             transform_and_round_points(graphics, pt, ptf, 3);
587             Polygon(graphics->hdc, pt, 3);
588
589             break;
590         case LineCapRoundAnchor:
591             dx = dy = ANCHOR_WIDTH * size / 2.0;
592
593             ptf[0].X = x2 - dx;
594             ptf[0].Y = y2 - dy;
595             ptf[1].X = x2 + dx;
596             ptf[1].Y = y2 + dy;
597
598             transform_and_round_points(graphics, pt, ptf, 2);
599             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
600
601             break;
602         case LineCapTriangle:
603             size = size / 2.0;
604             dx = cos(M_PI_2 + theta) * size;
605             dy = sin(M_PI_2 + theta) * size;
606
607             ptf[0].X = x2 - dx;
608             ptf[0].Y = y2 - dy;
609             ptf[1].X = x2 + dx;
610             ptf[1].Y = y2 + dy;
611
612             dx = cos(theta) * size;
613             dy = sin(theta) * size;
614
615             ptf[2].X = x2 + dx;
616             ptf[2].Y = y2 + dy;
617
618             transform_and_round_points(graphics, pt, ptf, 3);
619             Polygon(graphics->hdc, pt, 3);
620
621             break;
622         case LineCapRound:
623             dx = dy = size / 2.0;
624
625             ptf[0].X = x2 - dx;
626             ptf[0].Y = y2 - dy;
627             ptf[1].X = x2 + dx;
628             ptf[1].Y = y2 + dy;
629
630             dx = -cos(M_PI_2 + theta) * size;
631             dy = -sin(M_PI_2 + theta) * size;
632
633             ptf[2].X = x2 - dx;
634             ptf[2].Y = y2 - dy;
635             ptf[3].X = x2 + dx;
636             ptf[3].Y = y2 + dy;
637
638             transform_and_round_points(graphics, pt, ptf, 4);
639             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
640                 pt[2].y, pt[3].x, pt[3].y);
641
642             break;
643         case LineCapCustom:
644             if(!custom)
645                 break;
646
647             count = custom->pathdata.Count;
648             custptf = GdipAlloc(count * sizeof(PointF));
649             custpt = GdipAlloc(count * sizeof(POINT));
650             tp = GdipAlloc(count);
651
652             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
653                 goto custend;
654
655             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
656
657             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
658             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
659                              MatrixOrderAppend);
660             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
661             GdipTransformMatrixPoints(matrix, custptf, count);
662
663             transform_and_round_points(graphics, custpt, custptf, count);
664
665             for(i = 0; i < count; i++)
666                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
667
668             if(custom->fill){
669                 BeginPath(graphics->hdc);
670                 PolyDraw(graphics->hdc, custpt, tp, count);
671                 EndPath(graphics->hdc);
672                 StrokeAndFillPath(graphics->hdc);
673             }
674             else
675                 PolyDraw(graphics->hdc, custpt, tp, count);
676
677 custend:
678             GdipFree(custptf);
679             GdipFree(custpt);
680             GdipFree(tp);
681             GdipDeleteMatrix(matrix);
682             break;
683         default:
684             break;
685     }
686
687     if(!customstroke){
688         SelectObject(graphics->hdc, oldbrush);
689         SelectObject(graphics->hdc, oldpen);
690         DeleteObject(brush);
691         DeleteObject(pen);
692     }
693 }
694
695 /* Shortens the line by the given percent by changing x2, y2.
696  * If percent is > 1.0 then the line will change direction.
697  * If percent is negative it can lengthen the line. */
698 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
699 {
700     REAL dist, theta, dx, dy;
701
702     if((y1 == *y2) && (x1 == *x2))
703         return;
704
705     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
706     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
707     dx = cos(theta) * dist;
708     dy = sin(theta) * dist;
709
710     *x2 = *x2 + dx;
711     *y2 = *y2 + dy;
712 }
713
714 /* Shortens the line by the given amount by changing x2, y2.
715  * If the amount is greater than the distance, the line will become length 0.
716  * If the amount is negative, it can lengthen the line. */
717 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
718 {
719     REAL dx, dy, percent;
720
721     dx = *x2 - x1;
722     dy = *y2 - y1;
723     if(dx == 0 && dy == 0)
724         return;
725
726     percent = amt / sqrt(dx * dx + dy * dy);
727     if(percent >= 1.0){
728         *x2 = x1;
729         *y2 = y1;
730         return;
731     }
732
733     shorten_line_percent(x1, y1, x2, y2, percent);
734 }
735
736 /* Draws lines between the given points, and if caps is true then draws an endcap
737  * at the end of the last line. */
738 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
739     GDIPCONST GpPointF * pt, INT count, BOOL caps)
740 {
741     POINT *pti = NULL;
742     GpPointF *ptcopy = NULL;
743     GpStatus status = GenericError;
744
745     if(!count)
746         return Ok;
747
748     pti = GdipAlloc(count * sizeof(POINT));
749     ptcopy = GdipAlloc(count * sizeof(GpPointF));
750
751     if(!pti || !ptcopy){
752         status = OutOfMemory;
753         goto end;
754     }
755
756     memcpy(ptcopy, pt, count * sizeof(GpPointF));
757
758     if(caps){
759         if(pen->endcap == LineCapArrowAnchor)
760             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
761                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
762         else if((pen->endcap == LineCapCustom) && pen->customend)
763             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
764                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
765                              pen->customend->inset * pen->width);
766
767         if(pen->startcap == LineCapArrowAnchor)
768             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
769                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
770         else if((pen->startcap == LineCapCustom) && pen->customstart)
771             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
772                              &ptcopy[0].X, &ptcopy[0].Y,
773                              pen->customstart->inset * pen->width);
774
775         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
776                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
777         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
778                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
779     }
780
781     transform_and_round_points(graphics, pti, ptcopy, count);
782
783     if(Polyline(graphics->hdc, pti, count))
784         status = Ok;
785
786 end:
787     GdipFree(pti);
788     GdipFree(ptcopy);
789
790     return status;
791 }
792
793 /* Conducts a linear search to find the bezier points that will back off
794  * the endpoint of the curve by a distance of amt. Linear search works
795  * better than binary in this case because there are multiple solutions,
796  * and binary searches often find a bad one. I don't think this is what
797  * Windows does but short of rendering the bezier without GDI's help it's
798  * the best we can do. If rev then work from the start of the passed points
799  * instead of the end. */
800 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
801 {
802     GpPointF origpt[4];
803     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
804     INT i, first = 0, second = 1, third = 2, fourth = 3;
805
806     if(rev){
807         first = 3;
808         second = 2;
809         third = 1;
810         fourth = 0;
811     }
812
813     origx = pt[fourth].X;
814     origy = pt[fourth].Y;
815     memcpy(origpt, pt, sizeof(GpPointF) * 4);
816
817     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
818         /* reset bezier points to original values */
819         memcpy(pt, origpt, sizeof(GpPointF) * 4);
820         /* Perform magic on bezier points. Order is important here.*/
821         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
822         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
823         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
824         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
825         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
826         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
827
828         dx = pt[fourth].X - origx;
829         dy = pt[fourth].Y - origy;
830
831         diff = sqrt(dx * dx + dy * dy);
832         percent += 0.0005 * amt;
833     }
834 }
835
836 /* Draws bezier curves between given points, and if caps is true then draws an
837  * endcap at the end of the last line. */
838 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
839     GDIPCONST GpPointF * pt, INT count, BOOL caps)
840 {
841     POINT *pti;
842     GpPointF *ptcopy;
843     GpStatus status = GenericError;
844
845     if(!count)
846         return Ok;
847
848     pti = GdipAlloc(count * sizeof(POINT));
849     ptcopy = GdipAlloc(count * sizeof(GpPointF));
850
851     if(!pti || !ptcopy){
852         status = OutOfMemory;
853         goto end;
854     }
855
856     memcpy(ptcopy, pt, count * sizeof(GpPointF));
857
858     if(caps){
859         if(pen->endcap == LineCapArrowAnchor)
860             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
861         else if((pen->endcap == LineCapCustom) && pen->customend)
862             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
863                                FALSE);
864
865         if(pen->startcap == LineCapArrowAnchor)
866             shorten_bezier_amt(ptcopy, pen->width, TRUE);
867         else if((pen->startcap == LineCapCustom) && pen->customstart)
868             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
869
870         /* the direction of the line cap is parallel to the direction at the
871          * end of the bezier (which, if it has been shortened, is not the same
872          * as the direction from pt[count-2] to pt[count-1]) */
873         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
874             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
875             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
876             pt[count - 1].X, pt[count - 1].Y);
877
878         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
879             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
880             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
881     }
882
883     transform_and_round_points(graphics, pti, ptcopy, count);
884
885     PolyBezier(graphics->hdc, pti, count);
886
887     status = Ok;
888
889 end:
890     GdipFree(pti);
891     GdipFree(ptcopy);
892
893     return status;
894 }
895
896 /* Draws a combination of bezier curves and lines between points. */
897 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
898     GDIPCONST BYTE * types, INT count, BOOL caps)
899 {
900     POINT *pti = GdipAlloc(count * sizeof(POINT));
901     BYTE *tp = GdipAlloc(count);
902     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
903     INT i, j;
904     GpStatus status = GenericError;
905
906     if(!count){
907         status = Ok;
908         goto end;
909     }
910     if(!pti || !tp || !ptcopy){
911         status = OutOfMemory;
912         goto end;
913     }
914
915     for(i = 1; i < count; i++){
916         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
917             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
918                 || !(types[i + 1] & PathPointTypeBezier)){
919                 ERR("Bad bezier points\n");
920                 goto end;
921             }
922             i += 2;
923         }
924     }
925
926     memcpy(ptcopy, pt, count * sizeof(GpPointF));
927
928     /* If we are drawing caps, go through the points and adjust them accordingly,
929      * and draw the caps. */
930     if(caps){
931         switch(types[count - 1] & PathPointTypePathTypeMask){
932             case PathPointTypeBezier:
933                 if(pen->endcap == LineCapArrowAnchor)
934                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
935                 else if((pen->endcap == LineCapCustom) && pen->customend)
936                     shorten_bezier_amt(&ptcopy[count - 4],
937                                        pen->width * pen->customend->inset, FALSE);
938
939                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
940                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
941                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
942                     pt[count - 1].X, pt[count - 1].Y);
943
944                 break;
945             case PathPointTypeLine:
946                 if(pen->endcap == LineCapArrowAnchor)
947                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
948                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
949                                      pen->width);
950                 else if((pen->endcap == LineCapCustom) && pen->customend)
951                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
952                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
953                                      pen->customend->inset * pen->width);
954
955                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
956                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
957                          pt[count - 1].Y);
958
959                 break;
960             default:
961                 ERR("Bad path last point\n");
962                 goto end;
963         }
964
965         /* Find start of points */
966         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
967             == PathPointTypeStart); j++);
968
969         switch(types[j] & PathPointTypePathTypeMask){
970             case PathPointTypeBezier:
971                 if(pen->startcap == LineCapArrowAnchor)
972                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
973                 else if((pen->startcap == LineCapCustom) && pen->customstart)
974                     shorten_bezier_amt(&ptcopy[j - 1],
975                                        pen->width * pen->customstart->inset, TRUE);
976
977                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
978                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
979                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
980                     pt[j - 1].X, pt[j - 1].Y);
981
982                 break;
983             case PathPointTypeLine:
984                 if(pen->startcap == LineCapArrowAnchor)
985                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
986                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
987                                      pen->width);
988                 else if((pen->startcap == LineCapCustom) && pen->customstart)
989                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
990                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
991                                      pen->customstart->inset * pen->width);
992
993                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
994                          pt[j].X, pt[j].Y, pt[j - 1].X,
995                          pt[j - 1].Y);
996
997                 break;
998             default:
999                 ERR("Bad path points\n");
1000                 goto end;
1001         }
1002     }
1003
1004     transform_and_round_points(graphics, pti, ptcopy, count);
1005
1006     for(i = 0; i < count; i++){
1007         tp[i] = convert_path_point_type(types[i]);
1008     }
1009
1010     PolyDraw(graphics->hdc, pti, tp, count);
1011
1012     status = Ok;
1013
1014 end:
1015     GdipFree(pti);
1016     GdipFree(ptcopy);
1017     GdipFree(tp);
1018
1019     return status;
1020 }
1021
1022 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
1023 {
1024     GpStatus result;
1025
1026     BeginPath(graphics->hdc);
1027     result = draw_poly(graphics, NULL, path->pathdata.Points,
1028                        path->pathdata.Types, path->pathdata.Count, FALSE);
1029     EndPath(graphics->hdc);
1030     return result;
1031 }
1032
1033 typedef struct _GraphicsContainerItem {
1034     struct list entry;
1035     GraphicsContainer contid;
1036
1037     SmoothingMode smoothing;
1038     CompositingQuality compqual;
1039     InterpolationMode interpolation;
1040     CompositingMode compmode;
1041     TextRenderingHint texthint;
1042     REAL scale;
1043     GpUnit unit;
1044     PixelOffsetMode pixeloffset;
1045     UINT textcontrast;
1046     GpMatrix* worldtrans;
1047     GpRegion* clip;
1048 } GraphicsContainerItem;
1049
1050 static GpStatus init_container(GraphicsContainerItem** container,
1051         GDIPCONST GpGraphics* graphics){
1052     GpStatus sts;
1053
1054     *container = GdipAlloc(sizeof(GraphicsContainerItem));
1055     if(!(*container))
1056         return OutOfMemory;
1057
1058     (*container)->contid = graphics->contid + 1;
1059
1060     (*container)->smoothing = graphics->smoothing;
1061     (*container)->compqual = graphics->compqual;
1062     (*container)->interpolation = graphics->interpolation;
1063     (*container)->compmode = graphics->compmode;
1064     (*container)->texthint = graphics->texthint;
1065     (*container)->scale = graphics->scale;
1066     (*container)->unit = graphics->unit;
1067     (*container)->textcontrast = graphics->textcontrast;
1068     (*container)->pixeloffset = graphics->pixeloffset;
1069
1070     sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans);
1071     if(sts != Ok){
1072         GdipFree(*container);
1073         *container = NULL;
1074         return sts;
1075     }
1076
1077     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
1078     if(sts != Ok){
1079         GdipDeleteMatrix((*container)->worldtrans);
1080         GdipFree(*container);
1081         *container = NULL;
1082         return sts;
1083     }
1084
1085     return Ok;
1086 }
1087
1088 static void delete_container(GraphicsContainerItem* container){
1089     GdipDeleteMatrix(container->worldtrans);
1090     GdipDeleteRegion(container->clip);
1091     GdipFree(container);
1092 }
1093
1094 static GpStatus restore_container(GpGraphics* graphics,
1095         GDIPCONST GraphicsContainerItem* container){
1096     GpStatus sts;
1097     GpMatrix *newTrans;
1098     GpRegion *newClip;
1099
1100     sts = GdipCloneMatrix(container->worldtrans, &newTrans);
1101     if(sts != Ok)
1102         return sts;
1103
1104     sts = GdipCloneRegion(container->clip, &newClip);
1105     if(sts != Ok){
1106         GdipDeleteMatrix(newTrans);
1107         return sts;
1108     }
1109
1110     GdipDeleteMatrix(graphics->worldtrans);
1111     graphics->worldtrans = newTrans;
1112
1113     GdipDeleteRegion(graphics->clip);
1114     graphics->clip = newClip;
1115
1116     graphics->contid = container->contid - 1;
1117
1118     graphics->smoothing = container->smoothing;
1119     graphics->compqual = container->compqual;
1120     graphics->interpolation = container->interpolation;
1121     graphics->compmode = container->compmode;
1122     graphics->texthint = container->texthint;
1123     graphics->scale = container->scale;
1124     graphics->unit = container->unit;
1125     graphics->textcontrast = container->textcontrast;
1126     graphics->pixeloffset = container->pixeloffset;
1127
1128     return Ok;
1129 }
1130
1131 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
1132 {
1133     RECT wnd_rect;
1134
1135     if(graphics->hwnd) {
1136         if(!GetClientRect(graphics->hwnd, &wnd_rect))
1137             return GenericError;
1138
1139         rect->X = wnd_rect.left;
1140         rect->Y = wnd_rect.top;
1141         rect->Width = wnd_rect.right - wnd_rect.left;
1142         rect->Height = wnd_rect.bottom - wnd_rect.top;
1143     }else{
1144         rect->X = 0;
1145         rect->Y = 0;
1146         rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
1147         rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
1148     }
1149
1150     return Ok;
1151 }
1152
1153 /* on success, rgn will contain the region of the graphics object which
1154  * is visible after clipping has been applied */
1155 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
1156 {
1157     GpStatus stat;
1158     GpRectF rectf;
1159     GpRegion* tmp;
1160
1161     if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
1162         return stat;
1163
1164     if((stat = GdipCreateRegion(&tmp)) != Ok)
1165         return stat;
1166
1167     if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
1168         goto end;
1169
1170     if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
1171         goto end;
1172
1173     stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
1174
1175 end:
1176     GdipDeleteRegion(tmp);
1177     return stat;
1178 }
1179
1180 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
1181 {
1182     TRACE("(%p, %p)\n", hdc, graphics);
1183
1184     return GdipCreateFromHDC2(hdc, NULL, graphics);
1185 }
1186
1187 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
1188 {
1189     GpStatus retval;
1190
1191     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
1192
1193     if(hDevice != NULL) {
1194         FIXME("Don't know how to handle parameter hDevice\n");
1195         return NotImplemented;
1196     }
1197
1198     if(hdc == NULL)
1199         return OutOfMemory;
1200
1201     if(graphics == NULL)
1202         return InvalidParameter;
1203
1204     *graphics = GdipAlloc(sizeof(GpGraphics));
1205     if(!*graphics)  return OutOfMemory;
1206
1207     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
1208         GdipFree(*graphics);
1209         return retval;
1210     }
1211
1212     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
1213         GdipFree((*graphics)->worldtrans);
1214         GdipFree(*graphics);
1215         return retval;
1216     }
1217
1218     (*graphics)->hdc = hdc;
1219     (*graphics)->hwnd = WindowFromDC(hdc);
1220     (*graphics)->owndc = FALSE;
1221     (*graphics)->smoothing = SmoothingModeDefault;
1222     (*graphics)->compqual = CompositingQualityDefault;
1223     (*graphics)->interpolation = InterpolationModeDefault;
1224     (*graphics)->pixeloffset = PixelOffsetModeDefault;
1225     (*graphics)->compmode = CompositingModeSourceOver;
1226     (*graphics)->unit = UnitDisplay;
1227     (*graphics)->scale = 1.0;
1228     (*graphics)->busy = FALSE;
1229     (*graphics)->textcontrast = 4;
1230     list_init(&(*graphics)->containers);
1231     (*graphics)->contid = 0;
1232
1233     TRACE("<-- %p\n", *graphics);
1234
1235     return Ok;
1236 }
1237
1238 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
1239 {
1240     GpStatus ret;
1241     HDC hdc;
1242
1243     TRACE("(%p, %p)\n", hwnd, graphics);
1244
1245     hdc = GetDC(hwnd);
1246
1247     if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
1248     {
1249         ReleaseDC(hwnd, hdc);
1250         return ret;
1251     }
1252
1253     (*graphics)->hwnd = hwnd;
1254     (*graphics)->owndc = TRUE;
1255
1256     return Ok;
1257 }
1258
1259 /* FIXME: no icm handling */
1260 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
1261 {
1262     TRACE("(%p, %p)\n", hwnd, graphics);
1263
1264     return GdipCreateFromHWND(hwnd, graphics);
1265 }
1266
1267 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
1268     GpMetafile **metafile)
1269 {
1270     static int calls;
1271
1272     TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
1273
1274     if(!hemf || !metafile)
1275         return InvalidParameter;
1276
1277     if(!(calls++))
1278         FIXME("not implemented\n");
1279
1280     return NotImplemented;
1281 }
1282
1283 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1284     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1285 {
1286     IStream *stream = NULL;
1287     UINT read;
1288     BYTE* copy;
1289     HENHMETAFILE hemf;
1290     GpStatus retval = Ok;
1291
1292     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1293
1294     if(!hwmf || !metafile || !placeable)
1295         return InvalidParameter;
1296
1297     *metafile = NULL;
1298     read = GetMetaFileBitsEx(hwmf, 0, NULL);
1299     if(!read)
1300         return GenericError;
1301     copy = GdipAlloc(read);
1302     GetMetaFileBitsEx(hwmf, read, copy);
1303
1304     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1305     GdipFree(copy);
1306
1307     read = GetEnhMetaFileBits(hemf, 0, NULL);
1308     copy = GdipAlloc(read);
1309     GetEnhMetaFileBits(hemf, read, copy);
1310     DeleteEnhMetaFile(hemf);
1311
1312     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
1313         ERR("could not make stream\n");
1314         GdipFree(copy);
1315         retval = GenericError;
1316         goto err;
1317     }
1318
1319     *metafile = GdipAlloc(sizeof(GpMetafile));
1320     if(!*metafile){
1321         retval = OutOfMemory;
1322         goto err;
1323     }
1324
1325     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
1326         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
1327     {
1328         retval = GenericError;
1329         goto err;
1330     }
1331
1332
1333     (*metafile)->image.type = ImageTypeMetafile;
1334     memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID));
1335     (*metafile)->image.palette_flags = 0;
1336     (*metafile)->image.palette_count = 0;
1337     (*metafile)->image.palette_size = 0;
1338     (*metafile)->image.palette_entries = NULL;
1339     (*metafile)->image.xres = (REAL)placeable->Inch;
1340     (*metafile)->image.yres = (REAL)placeable->Inch;
1341     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
1342     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Top) / ((REAL) placeable->Inch);
1343     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
1344                     - placeable->BoundingBox.Left));
1345     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
1346                    - placeable->BoundingBox.Top));
1347     (*metafile)->unit = UnitPixel;
1348
1349     if(delete)
1350         DeleteMetaFile(hwmf);
1351
1352     TRACE("<-- %p\n", *metafile);
1353
1354 err:
1355     if (retval != Ok)
1356         GdipFree(*metafile);
1357     IStream_Release(stream);
1358     return retval;
1359 }
1360
1361 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1362     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1363 {
1364     HMETAFILE hmf = GetMetaFileW(file);
1365
1366     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1367
1368     if(!hmf) return InvalidParameter;
1369
1370     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1371 }
1372
1373 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
1374     GpMetafile **metafile)
1375 {
1376     FIXME("(%p, %p): stub\n", file, metafile);
1377     return NotImplemented;
1378 }
1379
1380 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
1381     GpMetafile **metafile)
1382 {
1383     FIXME("(%p, %p): stub\n", stream, metafile);
1384     return NotImplemented;
1385 }
1386
1387 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
1388     UINT access, IStream **stream)
1389 {
1390     DWORD dwMode;
1391     HRESULT ret;
1392
1393     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
1394
1395     if(!stream || !filename)
1396         return InvalidParameter;
1397
1398     if(access & GENERIC_WRITE)
1399         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
1400     else if(access & GENERIC_READ)
1401         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
1402     else
1403         return InvalidParameter;
1404
1405     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
1406
1407     return hresult_to_status(ret);
1408 }
1409
1410 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
1411 {
1412     GraphicsContainerItem *cont, *next;
1413     TRACE("(%p)\n", graphics);
1414
1415     if(!graphics) return InvalidParameter;
1416     if(graphics->busy) return ObjectBusy;
1417
1418     if(graphics->owndc)
1419         ReleaseDC(graphics->hwnd, graphics->hdc);
1420
1421     LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
1422         list_remove(&cont->entry);
1423         delete_container(cont);
1424     }
1425
1426     GdipDeleteRegion(graphics->clip);
1427     GdipDeleteMatrix(graphics->worldtrans);
1428     GdipFree(graphics);
1429
1430     return Ok;
1431 }
1432
1433 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
1434     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1435 {
1436     INT save_state, num_pts;
1437     GpPointF points[MAX_ARC_PTS];
1438     GpStatus retval;
1439
1440     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1441           width, height, startAngle, sweepAngle);
1442
1443     if(!graphics || !pen || width <= 0 || height <= 0)
1444         return InvalidParameter;
1445
1446     if(graphics->busy)
1447         return ObjectBusy;
1448
1449     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
1450
1451     save_state = prepare_dc(graphics, pen);
1452
1453     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
1454
1455     restore_dc(graphics, save_state);
1456
1457     return retval;
1458 }
1459
1460 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
1461     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1462 {
1463     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1464           width, height, startAngle, sweepAngle);
1465
1466     return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1467 }
1468
1469 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
1470     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
1471 {
1472     INT save_state;
1473     GpPointF pt[4];
1474     GpStatus retval;
1475
1476     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
1477           x2, y2, x3, y3, x4, y4);
1478
1479     if(!graphics || !pen)
1480         return InvalidParameter;
1481
1482     if(graphics->busy)
1483         return ObjectBusy;
1484
1485     pt[0].X = x1;
1486     pt[0].Y = y1;
1487     pt[1].X = x2;
1488     pt[1].Y = y2;
1489     pt[2].X = x3;
1490     pt[2].Y = y3;
1491     pt[3].X = x4;
1492     pt[3].Y = y4;
1493
1494     save_state = prepare_dc(graphics, pen);
1495
1496     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1497
1498     restore_dc(graphics, save_state);
1499
1500     return retval;
1501 }
1502
1503 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
1504     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
1505 {
1506     INT save_state;
1507     GpPointF pt[4];
1508     GpStatus retval;
1509
1510     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1511           x2, y2, x3, y3, x4, y4);
1512
1513     if(!graphics || !pen)
1514         return InvalidParameter;
1515
1516     if(graphics->busy)
1517         return ObjectBusy;
1518
1519     pt[0].X = x1;
1520     pt[0].Y = y1;
1521     pt[1].X = x2;
1522     pt[1].Y = y2;
1523     pt[2].X = x3;
1524     pt[2].Y = y3;
1525     pt[3].X = x4;
1526     pt[3].Y = y4;
1527
1528     save_state = prepare_dc(graphics, pen);
1529
1530     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1531
1532     restore_dc(graphics, save_state);
1533
1534     return retval;
1535 }
1536
1537 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1538     GDIPCONST GpPointF *points, INT count)
1539 {
1540     INT i;
1541     GpStatus ret;
1542
1543     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1544
1545     if(!graphics || !pen || !points || (count <= 0))
1546         return InvalidParameter;
1547
1548     if(graphics->busy)
1549         return ObjectBusy;
1550
1551     for(i = 0; i < floor(count / 4); i++){
1552         ret = GdipDrawBezier(graphics, pen,
1553                              points[4*i].X, points[4*i].Y,
1554                              points[4*i + 1].X, points[4*i + 1].Y,
1555                              points[4*i + 2].X, points[4*i + 2].Y,
1556                              points[4*i + 3].X, points[4*i + 3].Y);
1557         if(ret != Ok)
1558             return ret;
1559     }
1560
1561     return Ok;
1562 }
1563
1564 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1565     GDIPCONST GpPoint *points, INT count)
1566 {
1567     GpPointF *pts;
1568     GpStatus ret;
1569     INT i;
1570
1571     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1572
1573     if(!graphics || !pen || !points || (count <= 0))
1574         return InvalidParameter;
1575
1576     if(graphics->busy)
1577         return ObjectBusy;
1578
1579     pts = GdipAlloc(sizeof(GpPointF) * count);
1580     if(!pts)
1581         return OutOfMemory;
1582
1583     for(i = 0; i < count; i++){
1584         pts[i].X = (REAL)points[i].X;
1585         pts[i].Y = (REAL)points[i].Y;
1586     }
1587
1588     ret = GdipDrawBeziers(graphics,pen,pts,count);
1589
1590     GdipFree(pts);
1591
1592     return ret;
1593 }
1594
1595 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1596     GDIPCONST GpPointF *points, INT count)
1597 {
1598     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1599
1600     return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1601 }
1602
1603 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1604     GDIPCONST GpPoint *points, INT count)
1605 {
1606     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1607
1608     return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1609 }
1610
1611 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1612     GDIPCONST GpPointF *points, INT count, REAL tension)
1613 {
1614     GpPath *path;
1615     GpStatus stat;
1616
1617     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1618
1619     if(!graphics || !pen || !points || count <= 0)
1620         return InvalidParameter;
1621
1622     if(graphics->busy)
1623         return ObjectBusy;
1624
1625     if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1626         return stat;
1627
1628     stat = GdipAddPathClosedCurve2(path, points, count, tension);
1629     if(stat != Ok){
1630         GdipDeletePath(path);
1631         return stat;
1632     }
1633
1634     stat = GdipDrawPath(graphics, pen, path);
1635
1636     GdipDeletePath(path);
1637
1638     return stat;
1639 }
1640
1641 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1642     GDIPCONST GpPoint *points, INT count, REAL tension)
1643 {
1644     GpPointF *ptf;
1645     GpStatus stat;
1646     INT i;
1647
1648     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1649
1650     if(!points || count <= 0)
1651         return InvalidParameter;
1652
1653     ptf = GdipAlloc(sizeof(GpPointF)*count);
1654     if(!ptf)
1655         return OutOfMemory;
1656
1657     for(i = 0; i < count; i++){
1658         ptf[i].X = (REAL)points[i].X;
1659         ptf[i].Y = (REAL)points[i].Y;
1660     }
1661
1662     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1663
1664     GdipFree(ptf);
1665
1666     return stat;
1667 }
1668
1669 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1670     GDIPCONST GpPointF *points, INT count)
1671 {
1672     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1673
1674     return GdipDrawCurve2(graphics,pen,points,count,1.0);
1675 }
1676
1677 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1678     GDIPCONST GpPoint *points, INT count)
1679 {
1680     GpPointF *pointsF;
1681     GpStatus ret;
1682     INT i;
1683
1684     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1685
1686     if(!points)
1687         return InvalidParameter;
1688
1689     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1690     if(!pointsF)
1691         return OutOfMemory;
1692
1693     for(i = 0; i < count; i++){
1694         pointsF[i].X = (REAL)points[i].X;
1695         pointsF[i].Y = (REAL)points[i].Y;
1696     }
1697
1698     ret = GdipDrawCurve(graphics,pen,pointsF,count);
1699     GdipFree(pointsF);
1700
1701     return ret;
1702 }
1703
1704 /* Approximates cardinal spline with Bezier curves. */
1705 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1706     GDIPCONST GpPointF *points, INT count, REAL tension)
1707 {
1708     /* PolyBezier expects count*3-2 points. */
1709     INT i, len_pt = count*3-2, save_state;
1710     GpPointF *pt;
1711     REAL x1, x2, y1, y2;
1712     GpStatus retval;
1713
1714     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1715
1716     if(!graphics || !pen)
1717         return InvalidParameter;
1718
1719     if(graphics->busy)
1720         return ObjectBusy;
1721
1722     if(count < 2)
1723         return InvalidParameter;
1724
1725     pt = GdipAlloc(len_pt * sizeof(GpPointF));
1726     if(!pt)
1727         return OutOfMemory;
1728
1729     tension = tension * TENSION_CONST;
1730
1731     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1732         tension, &x1, &y1);
1733
1734     pt[0].X = points[0].X;
1735     pt[0].Y = points[0].Y;
1736     pt[1].X = x1;
1737     pt[1].Y = y1;
1738
1739     for(i = 0; i < count-2; i++){
1740         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1741
1742         pt[3*i+2].X = x1;
1743         pt[3*i+2].Y = y1;
1744         pt[3*i+3].X = points[i+1].X;
1745         pt[3*i+3].Y = points[i+1].Y;
1746         pt[3*i+4].X = x2;
1747         pt[3*i+4].Y = y2;
1748     }
1749
1750     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
1751         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
1752
1753     pt[len_pt-2].X = x1;
1754     pt[len_pt-2].Y = y1;
1755     pt[len_pt-1].X = points[count-1].X;
1756     pt[len_pt-1].Y = points[count-1].Y;
1757
1758     save_state = prepare_dc(graphics, pen);
1759
1760     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1761
1762     GdipFree(pt);
1763     restore_dc(graphics, save_state);
1764
1765     return retval;
1766 }
1767
1768 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1769     GDIPCONST GpPoint *points, INT count, REAL tension)
1770 {
1771     GpPointF *pointsF;
1772     GpStatus ret;
1773     INT i;
1774
1775     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1776
1777     if(!points)
1778         return InvalidParameter;
1779
1780     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1781     if(!pointsF)
1782         return OutOfMemory;
1783
1784     for(i = 0; i < count; i++){
1785         pointsF[i].X = (REAL)points[i].X;
1786         pointsF[i].Y = (REAL)points[i].Y;
1787     }
1788
1789     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1790     GdipFree(pointsF);
1791
1792     return ret;
1793 }
1794
1795 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
1796     GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
1797     REAL tension)
1798 {
1799     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1800
1801     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1802         return InvalidParameter;
1803     }
1804
1805     return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
1806 }
1807
1808 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
1809     GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
1810     REAL tension)
1811 {
1812     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1813
1814     if(count < 0){
1815         return OutOfMemory;
1816     }
1817
1818     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1819         return InvalidParameter;
1820     }
1821
1822     return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
1823 }
1824
1825 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1826     REAL y, REAL width, REAL height)
1827 {
1828     INT save_state;
1829     GpPointF ptf[2];
1830     POINT pti[2];
1831
1832     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1833
1834     if(!graphics || !pen)
1835         return InvalidParameter;
1836
1837     if(graphics->busy)
1838         return ObjectBusy;
1839
1840     ptf[0].X = x;
1841     ptf[0].Y = y;
1842     ptf[1].X = x + width;
1843     ptf[1].Y = y + height;
1844
1845     save_state = prepare_dc(graphics, pen);
1846     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1847
1848     transform_and_round_points(graphics, pti, ptf, 2);
1849
1850     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1851
1852     restore_dc(graphics, save_state);
1853
1854     return Ok;
1855 }
1856
1857 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1858     INT y, INT width, INT height)
1859 {
1860     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1861
1862     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1863 }
1864
1865
1866 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1867 {
1868     UINT width, height;
1869     GpPointF points[3];
1870
1871     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1872
1873     if(!graphics || !image)
1874         return InvalidParameter;
1875
1876     GdipGetImageWidth(image, &width);
1877     GdipGetImageHeight(image, &height);
1878
1879     /* FIXME: we should use the graphics and image dpi, somehow */
1880
1881     points[0].X = points[2].X = x;
1882     points[0].Y = points[1].Y = y;
1883     points[1].X = x + width;
1884     points[2].Y = y + height;
1885
1886     return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height,
1887         UnitPixel, NULL, NULL, NULL);
1888 }
1889
1890 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1891     INT y)
1892 {
1893     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1894
1895     return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
1896 }
1897
1898 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
1899     REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
1900     GpUnit srcUnit)
1901 {
1902     GpPointF points[3];
1903     TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1904
1905     points[0].X = points[2].X = x;
1906     points[0].Y = points[1].Y = y;
1907
1908     /* FIXME: convert image coordinates to Graphics coordinates? */
1909     points[1].X = x + srcwidth;
1910     points[2].Y = y + srcheight;
1911
1912     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1913         srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
1914 }
1915
1916 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
1917     INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
1918     GpUnit srcUnit)
1919 {
1920     return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1921 }
1922
1923 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
1924     GDIPCONST GpPointF *dstpoints, INT count)
1925 {
1926     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1927     return NotImplemented;
1928 }
1929
1930 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
1931     GDIPCONST GpPoint *dstpoints, INT count)
1932 {
1933     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1934     return NotImplemented;
1935 }
1936
1937 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1938      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1939      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1940      DrawImageAbort callback, VOID * callbackData)
1941 {
1942     GpPointF ptf[4];
1943     POINT pti[4];
1944     REAL dx, dy;
1945     GpStatus stat;
1946
1947     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1948           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1949           callbackData);
1950
1951     if(!graphics || !image || !points || count != 3)
1952          return InvalidParameter;
1953
1954     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
1955         debugstr_pointf(&points[2]));
1956
1957     memcpy(ptf, points, 3 * sizeof(GpPointF));
1958     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
1959     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
1960     transform_and_round_points(graphics, pti, ptf, 4);
1961
1962     if (image->picture)
1963     {
1964         /* FIXME: partially implemented (only works for rectangular parallelograms) */
1965         if(srcUnit == UnitInch)
1966             dx = dy = (REAL) INCH_HIMETRIC;
1967         else if(srcUnit == UnitPixel){
1968             dx = ((REAL) INCH_HIMETRIC) /
1969                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1970             dy = ((REAL) INCH_HIMETRIC) /
1971                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1972         }
1973         else
1974             return NotImplemented;
1975
1976         if(IPicture_Render(image->picture, graphics->hdc,
1977             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1978             srcx * dx, srcy * dy,
1979             srcwidth * dx, srcheight * dy,
1980             NULL) != S_OK){
1981             if(callback)
1982                 callback(callbackData);
1983             return GenericError;
1984         }
1985     }
1986     else if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
1987     {
1988         GpBitmap* bitmap = (GpBitmap*)image;
1989         int use_software=0;
1990
1991         if (srcUnit == UnitInch)
1992             dx = dy = 96.0; /* FIXME: use the image resolution */
1993         else if (srcUnit == UnitPixel)
1994             dx = dy = 1.0;
1995         else
1996             return NotImplemented;
1997
1998         if (imageAttributes ||
1999             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
2000             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X)
2001             use_software = 1;
2002
2003         if (use_software)
2004         {
2005             RECT src_area, dst_area;
2006             int i, x, y, stride;
2007             GpMatrix *dst_to_src;
2008             REAL m11, m12, m21, m22, mdx, mdy;
2009             LPBYTE data;
2010
2011             src_area.left = srcx*dx;
2012             src_area.top = srcy*dy;
2013             src_area.right = (srcx+srcwidth)*dx;
2014             src_area.bottom = (srcy+srcheight)*dy;
2015
2016             dst_area.left = dst_area.right = pti[0].x;
2017             dst_area.top = dst_area.bottom = pti[0].y;
2018             for (i=1; i<4; i++)
2019             {
2020                 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2021                 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2022                 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2023                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2024             }
2025
2026             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2027             m21 = (ptf[2].X - ptf[0].X) / srcheight;
2028             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2029             m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2030             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2031             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2032
2033             stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
2034             if (stat != Ok) return stat;
2035
2036             stat = GdipInvertMatrix(dst_to_src);
2037             if (stat != Ok)
2038             {
2039                 GdipDeleteMatrix(dst_to_src);
2040                 return stat;
2041             }
2042
2043             data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
2044             if (!data)
2045             {
2046                 GdipDeleteMatrix(dst_to_src);
2047                 return OutOfMemory;
2048             }
2049
2050             stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
2051
2052             if (imageAttributes &&
2053                 (imageAttributes->wrap != WrapModeClamp ||
2054                  imageAttributes->outside_color != 0x00000000 ||
2055                  imageAttributes->clamp))
2056             {
2057                 static int fixme;
2058                 if (!fixme++)
2059                     FIXME("Image wrap mode not implemented\n");
2060             }
2061
2062             for (x=dst_area.left; x<dst_area.right; x++)
2063             {
2064                 for (y=dst_area.top; y<dst_area.bottom; y++)
2065                 {
2066                     GpPointF src_pointf;
2067                     int src_x, src_y;
2068                     ARGB *src_color;
2069
2070                     src_pointf.X = x;
2071                     src_pointf.Y = y;
2072
2073                     GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
2074
2075                     src_x = roundr(src_pointf.X);
2076                     src_y = roundr(src_pointf.Y);
2077
2078                     src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2079
2080                     if (src_x < src_area.left || src_x >= src_area.right ||
2081                         src_y < src_area.top || src_y >= src_area.bottom)
2082                         *src_color = 0;
2083                     else
2084                         GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
2085                 }
2086             }
2087
2088             GdipDeleteMatrix(dst_to_src);
2089
2090             if (imageAttributes)
2091             {
2092                 if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
2093                     imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
2094                 {
2095                     const struct color_key *key;
2096                     BYTE min_blue, min_green, min_red;
2097                     BYTE max_blue, max_green, max_red;
2098
2099                     if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled)
2100                         key = &imageAttributes->colorkeys[ColorAdjustTypeBitmap];
2101                     else
2102                         key = &imageAttributes->colorkeys[ColorAdjustTypeDefault];
2103
2104                     min_blue = key->low&0xff;
2105                     min_green = (key->low>>8)&0xff;
2106                     min_red = (key->low>>16)&0xff;
2107
2108                     max_blue = key->high&0xff;
2109                     max_green = (key->high>>8)&0xff;
2110                     max_red = (key->high>>16)&0xff;
2111
2112                     for (x=dst_area.left; x<dst_area.right; x++)
2113                         for (y=dst_area.top; y<dst_area.bottom; y++)
2114                         {
2115                             ARGB *src_color;
2116                             BYTE blue, green, red;
2117                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2118                             blue = *src_color&0xff;
2119                             green = (*src_color>>8)&0xff;
2120                             red = (*src_color>>16)&0xff;
2121                             if (blue >= min_blue && green >= min_green && red >= min_red &&
2122                                 blue <= max_blue && green <= max_green && red <= max_red)
2123                                 *src_color = 0x00000000;
2124                         }
2125                 }
2126
2127                 if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
2128                     imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
2129                 {
2130                     const struct color_remap_table *table;
2131
2132                     if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
2133                         table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
2134                     else
2135                         table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
2136
2137                     for (x=dst_area.left; x<dst_area.right; x++)
2138                         for (y=dst_area.top; y<dst_area.bottom; y++)
2139                         {
2140                             ARGB *src_color;
2141                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2142                             for (i=0; i<table->mapsize; i++)
2143                             {
2144                                 if (*src_color == table->colormap[i].oldColor.Argb)
2145                                 {
2146                                     *src_color = table->colormap[i].newColor.Argb;
2147                                     break;
2148                                 }
2149                             }
2150                         }
2151                 }
2152
2153                 if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
2154                     imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
2155                 {
2156                     static int fixme;
2157                     if (!fixme++)
2158                         FIXME("Color transforms not implemented\n");
2159                 }
2160
2161                 if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
2162                     imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
2163                 {
2164                     static int fixme;
2165                     if (!fixme++)
2166                         FIXME("Gamma adjustment not implemented\n");
2167                 }
2168             }
2169
2170             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
2171                 data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
2172
2173             GdipFree(data);
2174
2175             return stat;
2176         }
2177         else
2178         {
2179             HDC hdc;
2180             int temp_hdc=0, temp_bitmap=0;
2181             HBITMAP hbitmap, old_hbm=NULL;
2182
2183             if (!(bitmap->format == PixelFormat16bppRGB555 ||
2184                   bitmap->format == PixelFormat24bppRGB ||
2185                   bitmap->format == PixelFormat32bppRGB ||
2186                   bitmap->format == PixelFormat32bppPARGB))
2187             {
2188                 BITMAPINFOHEADER bih;
2189                 BYTE *temp_bits;
2190                 PixelFormat dst_format;
2191
2192                 /* we can't draw a bitmap of this format directly */
2193                 hdc = CreateCompatibleDC(0);
2194                 temp_hdc = 1;
2195                 temp_bitmap = 1;
2196
2197                 bih.biSize = sizeof(BITMAPINFOHEADER);
2198                 bih.biWidth = bitmap->width;
2199                 bih.biHeight = -bitmap->height;
2200                 bih.biPlanes = 1;
2201                 bih.biBitCount = 32;
2202                 bih.biCompression = BI_RGB;
2203                 bih.biSizeImage = 0;
2204                 bih.biXPelsPerMeter = 0;
2205                 bih.biYPelsPerMeter = 0;
2206                 bih.biClrUsed = 0;
2207                 bih.biClrImportant = 0;
2208
2209                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
2210                     (void**)&temp_bits, NULL, 0);
2211
2212                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2213                     dst_format = PixelFormat32bppPARGB;
2214                 else
2215                     dst_format = PixelFormat32bppRGB;
2216
2217                 convert_pixels(bitmap->width, bitmap->height,
2218                     bitmap->width*4, temp_bits, dst_format,
2219                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
2220             }
2221             else
2222             {
2223                 hbitmap = bitmap->hbitmap;
2224                 hdc = bitmap->hdc;
2225                 temp_hdc = (hdc == 0);
2226             }
2227
2228             if (temp_hdc)
2229             {
2230                 if (!hdc) hdc = CreateCompatibleDC(0);
2231                 old_hbm = SelectObject(hdc, hbitmap);
2232             }
2233
2234             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2235             {
2236                 BLENDFUNCTION bf;
2237
2238                 bf.BlendOp = AC_SRC_OVER;
2239                 bf.BlendFlags = 0;
2240                 bf.SourceConstantAlpha = 255;
2241                 bf.AlphaFormat = AC_SRC_ALPHA;
2242
2243                 GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2244                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
2245             }
2246             else
2247             {
2248                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2249                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
2250             }
2251
2252             if (temp_hdc)
2253             {
2254                 SelectObject(hdc, old_hbm);
2255                 DeleteDC(hdc);
2256             }
2257
2258             if (temp_bitmap)
2259                 DeleteObject(hbitmap);
2260         }
2261     }
2262     else
2263     {
2264         ERR("GpImage with no IPicture or HBITMAP?!\n");
2265         return NotImplemented;
2266     }
2267
2268     return Ok;
2269 }
2270
2271 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
2272      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
2273      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2274      DrawImageAbort callback, VOID * callbackData)
2275 {
2276     GpPointF pointsF[3];
2277     INT i;
2278
2279     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
2280           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2281           callbackData);
2282
2283     if(!points || count!=3)
2284         return InvalidParameter;
2285
2286     for(i = 0; i < count; i++){
2287         pointsF[i].X = (REAL)points[i].X;
2288         pointsF[i].Y = (REAL)points[i].Y;
2289     }
2290
2291     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
2292                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
2293                                    callback, callbackData);
2294 }
2295
2296 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
2297     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
2298     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
2299     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
2300     VOID * callbackData)
2301 {
2302     GpPointF points[3];
2303
2304     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
2305           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2306           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2307
2308     points[0].X = dstx;
2309     points[0].Y = dsty;
2310     points[1].X = dstx + dstwidth;
2311     points[1].Y = dsty;
2312     points[2].X = dstx;
2313     points[2].Y = dsty + dstheight;
2314
2315     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2316                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2317 }
2318
2319 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
2320         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
2321         INT srcwidth, INT srcheight, GpUnit srcUnit,
2322         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
2323         VOID * callbackData)
2324 {
2325     GpPointF points[3];
2326
2327     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
2328           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2329           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2330
2331     points[0].X = dstx;
2332     points[0].Y = dsty;
2333     points[1].X = dstx + dstwidth;
2334     points[1].Y = dsty;
2335     points[2].X = dstx;
2336     points[2].Y = dsty + dstheight;
2337
2338     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2339                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2340 }
2341
2342 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
2343     REAL x, REAL y, REAL width, REAL height)
2344 {
2345     RectF bounds;
2346     GpUnit unit;
2347     GpStatus ret;
2348
2349     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
2350
2351     if(!graphics || !image)
2352         return InvalidParameter;
2353
2354     ret = GdipGetImageBounds(image, &bounds, &unit);
2355     if(ret != Ok)
2356         return ret;
2357
2358     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
2359                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
2360                                  unit, NULL, NULL, NULL);
2361 }
2362
2363 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
2364     INT x, INT y, INT width, INT height)
2365 {
2366     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
2367
2368     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
2369 }
2370
2371 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
2372     REAL y1, REAL x2, REAL y2)
2373 {
2374     INT save_state;
2375     GpPointF pt[2];
2376     GpStatus retval;
2377
2378     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
2379
2380     if(!pen || !graphics)
2381         return InvalidParameter;
2382
2383     if(graphics->busy)
2384         return ObjectBusy;
2385
2386     pt[0].X = x1;
2387     pt[0].Y = y1;
2388     pt[1].X = x2;
2389     pt[1].Y = y2;
2390
2391     save_state = prepare_dc(graphics, pen);
2392
2393     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2394
2395     restore_dc(graphics, save_state);
2396
2397     return retval;
2398 }
2399
2400 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
2401     INT y1, INT x2, INT y2)
2402 {
2403     INT save_state;
2404     GpPointF pt[2];
2405     GpStatus retval;
2406
2407     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
2408
2409     if(!pen || !graphics)
2410         return InvalidParameter;
2411
2412     if(graphics->busy)
2413         return ObjectBusy;
2414
2415     pt[0].X = (REAL)x1;
2416     pt[0].Y = (REAL)y1;
2417     pt[1].X = (REAL)x2;
2418     pt[1].Y = (REAL)y2;
2419
2420     save_state = prepare_dc(graphics, pen);
2421
2422     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2423
2424     restore_dc(graphics, save_state);
2425
2426     return retval;
2427 }
2428
2429 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
2430     GpPointF *points, INT count)
2431 {
2432     INT save_state;
2433     GpStatus retval;
2434
2435     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2436
2437     if(!pen || !graphics || (count < 2))
2438         return InvalidParameter;
2439
2440     if(graphics->busy)
2441         return ObjectBusy;
2442
2443     save_state = prepare_dc(graphics, pen);
2444
2445     retval = draw_polyline(graphics, pen, points, count, TRUE);
2446
2447     restore_dc(graphics, save_state);
2448
2449     return retval;
2450 }
2451
2452 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
2453     GpPoint *points, INT count)
2454 {
2455     INT save_state;
2456     GpStatus retval;
2457     GpPointF *ptf = NULL;
2458     int i;
2459
2460     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2461
2462     if(!pen || !graphics || (count < 2))
2463         return InvalidParameter;
2464
2465     if(graphics->busy)
2466         return ObjectBusy;
2467
2468     ptf = GdipAlloc(count * sizeof(GpPointF));
2469     if(!ptf) return OutOfMemory;
2470
2471     for(i = 0; i < count; i ++){
2472         ptf[i].X = (REAL) points[i].X;
2473         ptf[i].Y = (REAL) points[i].Y;
2474     }
2475
2476     save_state = prepare_dc(graphics, pen);
2477
2478     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
2479
2480     restore_dc(graphics, save_state);
2481
2482     GdipFree(ptf);
2483     return retval;
2484 }
2485
2486 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
2487 {
2488     INT save_state;
2489     GpStatus retval;
2490
2491     TRACE("(%p, %p, %p)\n", graphics, pen, path);
2492
2493     if(!pen || !graphics)
2494         return InvalidParameter;
2495
2496     if(graphics->busy)
2497         return ObjectBusy;
2498
2499     save_state = prepare_dc(graphics, pen);
2500
2501     retval = draw_poly(graphics, pen, path->pathdata.Points,
2502                        path->pathdata.Types, path->pathdata.Count, TRUE);
2503
2504     restore_dc(graphics, save_state);
2505
2506     return retval;
2507 }
2508
2509 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
2510     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2511 {
2512     INT save_state;
2513
2514     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2515             width, height, startAngle, sweepAngle);
2516
2517     if(!graphics || !pen)
2518         return InvalidParameter;
2519
2520     if(graphics->busy)
2521         return ObjectBusy;
2522
2523     save_state = prepare_dc(graphics, pen);
2524     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2525
2526     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2527
2528     restore_dc(graphics, save_state);
2529
2530     return Ok;
2531 }
2532
2533 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
2534     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2535 {
2536     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2537             width, height, startAngle, sweepAngle);
2538
2539     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2540 }
2541
2542 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
2543     REAL y, REAL width, REAL height)
2544 {
2545     INT save_state;
2546     GpPointF ptf[4];
2547     POINT pti[4];
2548
2549     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2550
2551     if(!pen || !graphics)
2552         return InvalidParameter;
2553
2554     if(graphics->busy)
2555         return ObjectBusy;
2556
2557     ptf[0].X = x;
2558     ptf[0].Y = y;
2559     ptf[1].X = x + width;
2560     ptf[1].Y = y;
2561     ptf[2].X = x + width;
2562     ptf[2].Y = y + height;
2563     ptf[3].X = x;
2564     ptf[3].Y = y + height;
2565
2566     save_state = prepare_dc(graphics, pen);
2567     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2568
2569     transform_and_round_points(graphics, pti, ptf, 4);
2570     Polygon(graphics->hdc, pti, 4);
2571
2572     restore_dc(graphics, save_state);
2573
2574     return Ok;
2575 }
2576
2577 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
2578     INT y, INT width, INT height)
2579 {
2580     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2581
2582     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2583 }
2584
2585 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
2586     GDIPCONST GpRectF* rects, INT count)
2587 {
2588     GpPointF *ptf;
2589     POINT *pti;
2590     INT save_state, i;
2591
2592     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2593
2594     if(!graphics || !pen || !rects || count < 1)
2595         return InvalidParameter;
2596
2597     if(graphics->busy)
2598         return ObjectBusy;
2599
2600     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
2601     pti = GdipAlloc(4 * count * sizeof(POINT));
2602
2603     if(!ptf || !pti){
2604         GdipFree(ptf);
2605         GdipFree(pti);
2606         return OutOfMemory;
2607     }
2608
2609     for(i = 0; i < count; i++){
2610         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
2611         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
2612         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
2613         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
2614     }
2615
2616     save_state = prepare_dc(graphics, pen);
2617     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2618
2619     transform_and_round_points(graphics, pti, ptf, 4 * count);
2620
2621     for(i = 0; i < count; i++)
2622         Polygon(graphics->hdc, &pti[4 * i], 4);
2623
2624     restore_dc(graphics, save_state);
2625
2626     GdipFree(ptf);
2627     GdipFree(pti);
2628
2629     return Ok;
2630 }
2631
2632 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
2633     GDIPCONST GpRect* rects, INT count)
2634 {
2635     GpRectF *rectsF;
2636     GpStatus ret;
2637     INT i;
2638
2639     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2640
2641     if(!rects || count<=0)
2642         return InvalidParameter;
2643
2644     rectsF = GdipAlloc(sizeof(GpRectF) * count);
2645     if(!rectsF)
2646         return OutOfMemory;
2647
2648     for(i = 0;i < count;i++){
2649         rectsF[i].X      = (REAL)rects[i].X;
2650         rectsF[i].Y      = (REAL)rects[i].Y;
2651         rectsF[i].Width  = (REAL)rects[i].Width;
2652         rectsF[i].Height = (REAL)rects[i].Height;
2653     }
2654
2655     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
2656     GdipFree(rectsF);
2657
2658     return ret;
2659 }
2660
2661 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2662     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2663 {
2664     GpPath *path;
2665     GpStatus stat;
2666
2667     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2668             count, tension, fill);
2669
2670     if(!graphics || !brush || !points)
2671         return InvalidParameter;
2672
2673     if(graphics->busy)
2674         return ObjectBusy;
2675
2676     if(count == 1)    /* Do nothing */
2677         return Ok;
2678
2679     stat = GdipCreatePath(fill, &path);
2680     if(stat != Ok)
2681         return stat;
2682
2683     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2684     if(stat != Ok){
2685         GdipDeletePath(path);
2686         return stat;
2687     }
2688
2689     stat = GdipFillPath(graphics, brush, path);
2690     if(stat != Ok){
2691         GdipDeletePath(path);
2692         return stat;
2693     }
2694
2695     GdipDeletePath(path);
2696
2697     return Ok;
2698 }
2699
2700 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2701     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2702 {
2703     GpPointF *ptf;
2704     GpStatus stat;
2705     INT i;
2706
2707     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2708             count, tension, fill);
2709
2710     if(!points || count == 0)
2711         return InvalidParameter;
2712
2713     if(count == 1)    /* Do nothing */
2714         return Ok;
2715
2716     ptf = GdipAlloc(sizeof(GpPointF)*count);
2717     if(!ptf)
2718         return OutOfMemory;
2719
2720     for(i = 0;i < count;i++){
2721         ptf[i].X = (REAL)points[i].X;
2722         ptf[i].Y = (REAL)points[i].Y;
2723     }
2724
2725     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2726
2727     GdipFree(ptf);
2728
2729     return stat;
2730 }
2731
2732 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2733     REAL y, REAL width, REAL height)
2734 {
2735     INT save_state;
2736     GpPointF ptf[2];
2737     POINT pti[2];
2738
2739     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2740
2741     if(!graphics || !brush)
2742         return InvalidParameter;
2743
2744     if(graphics->busy)
2745         return ObjectBusy;
2746
2747     ptf[0].X = x;
2748     ptf[0].Y = y;
2749     ptf[1].X = x + width;
2750     ptf[1].Y = y + height;
2751
2752     save_state = SaveDC(graphics->hdc);
2753     EndPath(graphics->hdc);
2754
2755     transform_and_round_points(graphics, pti, ptf, 2);
2756
2757     BeginPath(graphics->hdc);
2758     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2759     EndPath(graphics->hdc);
2760
2761     brush_fill_path(graphics, brush);
2762
2763     RestoreDC(graphics->hdc, save_state);
2764
2765     return Ok;
2766 }
2767
2768 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2769     INT y, INT width, INT height)
2770 {
2771     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2772
2773     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2774 }
2775
2776 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2777 {
2778     INT save_state;
2779     GpStatus retval;
2780
2781     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2782
2783     if(!brush || !graphics || !path)
2784         return InvalidParameter;
2785
2786     if(graphics->busy)
2787         return ObjectBusy;
2788
2789     save_state = SaveDC(graphics->hdc);
2790     EndPath(graphics->hdc);
2791     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2792                                                                     : WINDING));
2793
2794     BeginPath(graphics->hdc);
2795     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2796                        path->pathdata.Types, path->pathdata.Count, FALSE);
2797
2798     if(retval != Ok)
2799         goto end;
2800
2801     EndPath(graphics->hdc);
2802     brush_fill_path(graphics, brush);
2803
2804     retval = Ok;
2805
2806 end:
2807     RestoreDC(graphics->hdc, save_state);
2808
2809     return retval;
2810 }
2811
2812 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2813     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2814 {
2815     INT save_state;
2816
2817     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2818             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2819
2820     if(!graphics || !brush)
2821         return InvalidParameter;
2822
2823     if(graphics->busy)
2824         return ObjectBusy;
2825
2826     save_state = SaveDC(graphics->hdc);
2827     EndPath(graphics->hdc);
2828
2829     BeginPath(graphics->hdc);
2830     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2831     EndPath(graphics->hdc);
2832
2833     brush_fill_path(graphics, brush);
2834
2835     RestoreDC(graphics->hdc, save_state);
2836
2837     return Ok;
2838 }
2839
2840 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2841     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2842 {
2843     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2844             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2845
2846     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2847 }
2848
2849 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2850     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2851 {
2852     INT save_state;
2853     GpPointF *ptf = NULL;
2854     POINT *pti = NULL;
2855     GpStatus retval = Ok;
2856
2857     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2858
2859     if(!graphics || !brush || !points || !count)
2860         return InvalidParameter;
2861
2862     if(graphics->busy)
2863         return ObjectBusy;
2864
2865     ptf = GdipAlloc(count * sizeof(GpPointF));
2866     pti = GdipAlloc(count * sizeof(POINT));
2867     if(!ptf || !pti){
2868         retval = OutOfMemory;
2869         goto end;
2870     }
2871
2872     memcpy(ptf, points, count * sizeof(GpPointF));
2873
2874     save_state = SaveDC(graphics->hdc);
2875     EndPath(graphics->hdc);
2876     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2877                                                                   : WINDING));
2878
2879     transform_and_round_points(graphics, pti, ptf, count);
2880
2881     BeginPath(graphics->hdc);
2882     Polygon(graphics->hdc, pti, count);
2883     EndPath(graphics->hdc);
2884
2885     brush_fill_path(graphics, brush);
2886
2887     RestoreDC(graphics->hdc, save_state);
2888
2889 end:
2890     GdipFree(ptf);
2891     GdipFree(pti);
2892
2893     return retval;
2894 }
2895
2896 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2897     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2898 {
2899     INT save_state, i;
2900     GpPointF *ptf = NULL;
2901     POINT *pti = NULL;
2902     GpStatus retval = Ok;
2903
2904     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2905
2906     if(!graphics || !brush || !points || !count)
2907         return InvalidParameter;
2908
2909     if(graphics->busy)
2910         return ObjectBusy;
2911
2912     ptf = GdipAlloc(count * sizeof(GpPointF));
2913     pti = GdipAlloc(count * sizeof(POINT));
2914     if(!ptf || !pti){
2915         retval = OutOfMemory;
2916         goto end;
2917     }
2918
2919     for(i = 0; i < count; i ++){
2920         ptf[i].X = (REAL) points[i].X;
2921         ptf[i].Y = (REAL) points[i].Y;
2922     }
2923
2924     save_state = SaveDC(graphics->hdc);
2925     EndPath(graphics->hdc);
2926     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2927                                                                   : WINDING));
2928
2929     transform_and_round_points(graphics, pti, ptf, count);
2930
2931     BeginPath(graphics->hdc);
2932     Polygon(graphics->hdc, pti, count);
2933     EndPath(graphics->hdc);
2934
2935     brush_fill_path(graphics, brush);
2936
2937     RestoreDC(graphics->hdc, save_state);
2938
2939 end:
2940     GdipFree(ptf);
2941     GdipFree(pti);
2942
2943     return retval;
2944 }
2945
2946 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2947     GDIPCONST GpPointF *points, INT count)
2948 {
2949     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2950
2951     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2952 }
2953
2954 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2955     GDIPCONST GpPoint *points, INT count)
2956 {
2957     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2958
2959     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2960 }
2961
2962 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2963     REAL x, REAL y, REAL width, REAL height)
2964 {
2965     INT save_state;
2966     GpPointF ptf[4];
2967     POINT pti[4];
2968
2969     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2970
2971     if(!graphics || !brush)
2972         return InvalidParameter;
2973
2974     if(graphics->busy)
2975         return ObjectBusy;
2976
2977     ptf[0].X = x;
2978     ptf[0].Y = y;
2979     ptf[1].X = x + width;
2980     ptf[1].Y = y;
2981     ptf[2].X = x + width;
2982     ptf[2].Y = y + height;
2983     ptf[3].X = x;
2984     ptf[3].Y = y + height;
2985
2986     save_state = SaveDC(graphics->hdc);
2987     EndPath(graphics->hdc);
2988
2989     transform_and_round_points(graphics, pti, ptf, 4);
2990
2991     BeginPath(graphics->hdc);
2992     Polygon(graphics->hdc, pti, 4);
2993     EndPath(graphics->hdc);
2994
2995     brush_fill_path(graphics, brush);
2996
2997     RestoreDC(graphics->hdc, save_state);
2998
2999     return Ok;
3000 }
3001
3002 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3003     INT x, INT y, INT width, INT height)
3004 {
3005     INT save_state;
3006     GpPointF ptf[4];
3007     POINT pti[4];
3008
3009     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3010
3011     if(!graphics || !brush)
3012         return InvalidParameter;
3013
3014     if(graphics->busy)
3015         return ObjectBusy;
3016
3017     ptf[0].X = x;
3018     ptf[0].Y = y;
3019     ptf[1].X = x + width;
3020     ptf[1].Y = y;
3021     ptf[2].X = x + width;
3022     ptf[2].Y = y + height;
3023     ptf[3].X = x;
3024     ptf[3].Y = y + height;
3025
3026     save_state = SaveDC(graphics->hdc);
3027     EndPath(graphics->hdc);
3028
3029     transform_and_round_points(graphics, pti, ptf, 4);
3030
3031     BeginPath(graphics->hdc);
3032     Polygon(graphics->hdc, pti, 4);
3033     EndPath(graphics->hdc);
3034
3035     brush_fill_path(graphics, brush);
3036
3037     RestoreDC(graphics->hdc, save_state);
3038
3039     return Ok;
3040 }
3041
3042 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3043     INT count)
3044 {
3045     GpStatus ret;
3046     INT i;
3047
3048     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3049
3050     if(!rects)
3051         return InvalidParameter;
3052
3053     for(i = 0; i < count; i++){
3054         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3055         if(ret != Ok)   return ret;
3056     }
3057
3058     return Ok;
3059 }
3060
3061 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3062     INT count)
3063 {
3064     GpRectF *rectsF;
3065     GpStatus ret;
3066     INT i;
3067
3068     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3069
3070     if(!rects || count <= 0)
3071         return InvalidParameter;
3072
3073     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3074     if(!rectsF)
3075         return OutOfMemory;
3076
3077     for(i = 0; i < count; i++){
3078         rectsF[i].X      = (REAL)rects[i].X;
3079         rectsF[i].Y      = (REAL)rects[i].Y;
3080         rectsF[i].X      = (REAL)rects[i].Width;
3081         rectsF[i].Height = (REAL)rects[i].Height;
3082     }
3083
3084     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3085     GdipFree(rectsF);
3086
3087     return ret;
3088 }
3089
3090 /*****************************************************************************
3091  * GdipFillRegion [GDIPLUS.@]
3092  */
3093 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3094         GpRegion* region)
3095 {
3096     INT save_state;
3097     GpStatus status;
3098     HRGN hrgn;
3099     RECT rc;
3100
3101     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3102
3103     if (!(graphics && brush && region))
3104         return InvalidParameter;
3105
3106     if(graphics->busy)
3107         return ObjectBusy;
3108
3109     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3110     if(status != Ok)
3111         return status;
3112
3113     save_state = SaveDC(graphics->hdc);
3114     EndPath(graphics->hdc);
3115
3116     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3117
3118     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3119     {
3120         BeginPath(graphics->hdc);
3121         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3122         EndPath(graphics->hdc);
3123
3124         brush_fill_path(graphics, brush);
3125     }
3126
3127     RestoreDC(graphics->hdc, save_state);
3128
3129     DeleteObject(hrgn);
3130
3131     return Ok;
3132 }
3133
3134 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3135 {
3136     TRACE("(%p,%u)\n", graphics, intention);
3137
3138     if(!graphics)
3139         return InvalidParameter;
3140
3141     if(graphics->busy)
3142         return ObjectBusy;
3143
3144     /* We have no internal operation queue, so there's no need to clear it. */
3145
3146     if (graphics->hdc)
3147         GdiFlush();
3148
3149     return Ok;
3150 }
3151
3152 /*****************************************************************************
3153  * GdipGetClipBounds [GDIPLUS.@]
3154  */
3155 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3156 {
3157     TRACE("(%p, %p)\n", graphics, rect);
3158
3159     if(!graphics)
3160         return InvalidParameter;
3161
3162     if(graphics->busy)
3163         return ObjectBusy;
3164
3165     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3166 }
3167
3168 /*****************************************************************************
3169  * GdipGetClipBoundsI [GDIPLUS.@]
3170  */
3171 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3172 {
3173     TRACE("(%p, %p)\n", graphics, rect);
3174
3175     if(!graphics)
3176         return InvalidParameter;
3177
3178     if(graphics->busy)
3179         return ObjectBusy;
3180
3181     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3182 }
3183
3184 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3185 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3186     CompositingMode *mode)
3187 {
3188     TRACE("(%p, %p)\n", graphics, mode);
3189
3190     if(!graphics || !mode)
3191         return InvalidParameter;
3192
3193     if(graphics->busy)
3194         return ObjectBusy;
3195
3196     *mode = graphics->compmode;
3197
3198     return Ok;
3199 }
3200
3201 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3202 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3203     CompositingQuality *quality)
3204 {
3205     TRACE("(%p, %p)\n", graphics, quality);
3206
3207     if(!graphics || !quality)
3208         return InvalidParameter;
3209
3210     if(graphics->busy)
3211         return ObjectBusy;
3212
3213     *quality = graphics->compqual;
3214
3215     return Ok;
3216 }
3217
3218 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
3219 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
3220     InterpolationMode *mode)
3221 {
3222     TRACE("(%p, %p)\n", graphics, mode);
3223
3224     if(!graphics || !mode)
3225         return InvalidParameter;
3226
3227     if(graphics->busy)
3228         return ObjectBusy;
3229
3230     *mode = graphics->interpolation;
3231
3232     return Ok;
3233 }
3234
3235 /* FIXME: Need to handle color depths less than 24bpp */
3236 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
3237 {
3238     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
3239
3240     if(!graphics || !argb)
3241         return InvalidParameter;
3242
3243     if(graphics->busy)
3244         return ObjectBusy;
3245
3246     return Ok;
3247 }
3248
3249 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
3250 {
3251     TRACE("(%p, %p)\n", graphics, scale);
3252
3253     if(!graphics || !scale)
3254         return InvalidParameter;
3255
3256     if(graphics->busy)
3257         return ObjectBusy;
3258
3259     *scale = graphics->scale;
3260
3261     return Ok;
3262 }
3263
3264 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
3265 {
3266     TRACE("(%p, %p)\n", graphics, unit);
3267
3268     if(!graphics || !unit)
3269         return InvalidParameter;
3270
3271     if(graphics->busy)
3272         return ObjectBusy;
3273
3274     *unit = graphics->unit;
3275
3276     return Ok;
3277 }
3278
3279 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
3280 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3281     *mode)
3282 {
3283     TRACE("(%p, %p)\n", graphics, mode);
3284
3285     if(!graphics || !mode)
3286         return InvalidParameter;
3287
3288     if(graphics->busy)
3289         return ObjectBusy;
3290
3291     *mode = graphics->pixeloffset;
3292
3293     return Ok;
3294 }
3295
3296 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
3297 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
3298 {
3299     TRACE("(%p, %p)\n", graphics, mode);
3300
3301     if(!graphics || !mode)
3302         return InvalidParameter;
3303
3304     if(graphics->busy)
3305         return ObjectBusy;
3306
3307     *mode = graphics->smoothing;
3308
3309     return Ok;
3310 }
3311
3312 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
3313 {
3314     TRACE("(%p, %p)\n", graphics, contrast);
3315
3316     if(!graphics || !contrast)
3317         return InvalidParameter;
3318
3319     *contrast = graphics->textcontrast;
3320
3321     return Ok;
3322 }
3323
3324 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
3325 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
3326     TextRenderingHint *hint)
3327 {
3328     TRACE("(%p, %p)\n", graphics, hint);
3329
3330     if(!graphics || !hint)
3331         return InvalidParameter;
3332
3333     if(graphics->busy)
3334         return ObjectBusy;
3335
3336     *hint = graphics->texthint;
3337
3338     return Ok;
3339 }
3340
3341 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
3342 {
3343     GpRegion *clip_rgn;
3344     GpStatus stat;
3345
3346     TRACE("(%p, %p)\n", graphics, rect);
3347
3348     if(!graphics || !rect)
3349         return InvalidParameter;
3350
3351     if(graphics->busy)
3352         return ObjectBusy;
3353
3354     /* intersect window and graphics clipping regions */
3355     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
3356         return stat;
3357
3358     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
3359         goto cleanup;
3360
3361     /* get bounds of the region */
3362     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
3363
3364 cleanup:
3365     GdipDeleteRegion(clip_rgn);
3366
3367     return stat;
3368 }
3369
3370 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3371 {
3372     GpRectF rectf;
3373     GpStatus stat;
3374
3375     TRACE("(%p, %p)\n", graphics, rect);
3376
3377     if(!graphics || !rect)
3378         return InvalidParameter;
3379
3380     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
3381     {
3382         rect->X = roundr(rectf.X);
3383         rect->Y = roundr(rectf.Y);
3384         rect->Width  = roundr(rectf.Width);
3385         rect->Height = roundr(rectf.Height);
3386     }
3387
3388     return stat;
3389 }
3390
3391 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3392 {
3393     TRACE("(%p, %p)\n", graphics, matrix);
3394
3395     if(!graphics || !matrix)
3396         return InvalidParameter;
3397
3398     if(graphics->busy)
3399         return ObjectBusy;
3400
3401     *matrix = *graphics->worldtrans;
3402     return Ok;
3403 }
3404
3405 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
3406 {
3407     GpSolidFill *brush;
3408     GpStatus stat;
3409     GpRectF wnd_rect;
3410
3411     TRACE("(%p, %x)\n", graphics, color);
3412
3413     if(!graphics)
3414         return InvalidParameter;
3415
3416     if(graphics->busy)
3417         return ObjectBusy;
3418
3419     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3420         return stat;
3421
3422     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
3423         GdipDeleteBrush((GpBrush*)brush);
3424         return stat;
3425     }
3426
3427     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
3428                                                  wnd_rect.Width, wnd_rect.Height);
3429
3430     GdipDeleteBrush((GpBrush*)brush);
3431
3432     return Ok;
3433 }
3434
3435 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3436 {
3437     TRACE("(%p, %p)\n", graphics, res);
3438
3439     if(!graphics || !res)
3440         return InvalidParameter;
3441
3442     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3443 }
3444
3445 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3446 {
3447     GpStatus stat;
3448     GpRegion* rgn;
3449     GpPointF pt;
3450
3451     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
3452
3453     if(!graphics || !result)
3454         return InvalidParameter;
3455
3456     if(graphics->busy)
3457         return ObjectBusy;
3458
3459     pt.X = x;
3460     pt.Y = y;
3461     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3462                    CoordinateSpaceWorld, &pt, 1)) != Ok)
3463         return stat;
3464
3465     if((stat = GdipCreateRegion(&rgn)) != Ok)
3466         return stat;
3467
3468     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3469         goto cleanup;
3470
3471     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
3472
3473 cleanup:
3474     GdipDeleteRegion(rgn);
3475     return stat;
3476 }
3477
3478 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3479 {
3480     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
3481 }
3482
3483 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
3484 {
3485     GpStatus stat;
3486     GpRegion* rgn;
3487     GpPointF pts[2];
3488
3489     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
3490
3491     if(!graphics || !result)
3492         return InvalidParameter;
3493
3494     if(graphics->busy)
3495         return ObjectBusy;
3496
3497     pts[0].X = x;
3498     pts[0].Y = y;
3499     pts[1].X = x + width;
3500     pts[1].Y = y + height;
3501
3502     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3503                     CoordinateSpaceWorld, pts, 2)) != Ok)
3504         return stat;
3505
3506     pts[1].X -= pts[0].X;
3507     pts[1].Y -= pts[0].Y;
3508
3509     if((stat = GdipCreateRegion(&rgn)) != Ok)
3510         return stat;
3511
3512     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3513         goto cleanup;
3514
3515     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
3516
3517 cleanup:
3518     GdipDeleteRegion(rgn);
3519     return stat;
3520 }
3521
3522 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
3523 {
3524     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
3525 }
3526
3527 typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
3528     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3529     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3530     INT lineno, const RectF *bounds, void *user_data);
3531
3532 static GpStatus gdip_format_string(GpGraphics *graphics,
3533     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3534     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3535     gdip_format_string_callback callback, void *user_data)
3536 {
3537     WCHAR* stringdup;
3538     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
3539         nheight, lineend, lineno = 0;
3540     RectF bounds;
3541     StringAlignment halign;
3542     GpStatus stat = Ok;
3543     SIZE size;
3544
3545     if(length == -1) length = lstrlenW(string);
3546
3547     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
3548     if(!stringdup) return OutOfMemory;
3549
3550     nwidth = roundr(rect->Width);
3551     nheight = roundr(rect->Height);
3552
3553     if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
3554     if (rect->Height >= INT_MAX || rect->Width < 0.5) nheight = INT_MAX;
3555
3556     for(i = 0, j = 0; i < length; i++){
3557         /* FIXME: This makes the indexes passed to callback inaccurate. */
3558         if(!isprintW(string[i]) && (string[i] != '\n'))
3559             continue;
3560
3561         stringdup[j] = string[i];
3562         j++;
3563     }
3564
3565     length = j;
3566
3567     if (format) halign = format->align;
3568     else halign = StringAlignmentNear;
3569
3570     while(sum < length){
3571         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
3572                               nwidth, &fit, NULL, &size);
3573         fitcpy = fit;
3574
3575         if(fit == 0)
3576             break;
3577
3578         for(lret = 0; lret < fit; lret++)
3579             if(*(stringdup + sum + lret) == '\n')
3580                 break;
3581
3582         /* Line break code (may look strange, but it imitates windows). */
3583         if(lret < fit)
3584             lineend = fit = lret;    /* this is not an off-by-one error */
3585         else if(fit < (length - sum)){
3586             if(*(stringdup + sum + fit) == ' ')
3587                 while(*(stringdup + sum + fit) == ' ')
3588                     fit++;
3589             else
3590                 while(*(stringdup + sum + fit - 1) != ' '){
3591                     fit--;
3592
3593                     if(*(stringdup + sum + fit) == '\t')
3594                         break;
3595
3596                     if(fit == 0){
3597                         fit = fitcpy;
3598                         break;
3599                     }
3600                 }
3601             lineend = fit;
3602             while(*(stringdup + sum + lineend - 1) == ' ' ||
3603                   *(stringdup + sum + lineend - 1) == '\t')
3604                 lineend--;
3605         }
3606         else
3607             lineend = fit;
3608
3609         GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
3610                               nwidth, &j, NULL, &size);
3611
3612         bounds.Width = size.cx;
3613
3614         if(height + size.cy > nheight)
3615             bounds.Height = nheight - (height + size.cy);
3616         else
3617             bounds.Height = size.cy;
3618
3619         bounds.Y = rect->Y + height;
3620
3621         switch (halign)
3622         {
3623         case StringAlignmentNear:
3624         default:
3625             bounds.X = rect->X;
3626             break;
3627         case StringAlignmentCenter:
3628             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
3629             break;
3630         case StringAlignmentFar:
3631             bounds.X = rect->X + rect->Width - bounds.Width;
3632             break;
3633         }
3634
3635         stat = callback(graphics, stringdup, sum, lineend,
3636             font, rect, format, lineno, &bounds, user_data);
3637
3638         if (stat != Ok)
3639             break;
3640
3641         sum += fit + (lret < fitcpy ? 1 : 0);
3642         height += size.cy;
3643         lineno++;
3644
3645         if(height > nheight)
3646             break;
3647
3648         /* Stop if this was a linewrap (but not if it was a linebreak). */
3649         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3650             break;
3651     }
3652
3653     GdipFree(stringdup);
3654
3655     return stat;
3656 }
3657
3658 struct measure_ranges_args {
3659     GpRegion **regions;
3660 };
3661
3662 static GpStatus measure_ranges_callback(GpGraphics *graphics,
3663     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3664     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3665     INT lineno, const RectF *bounds, void *user_data)
3666 {
3667     int i;
3668     GpStatus stat = Ok;
3669     struct measure_ranges_args *args = user_data;
3670
3671     for (i=0; i<format->range_count; i++)
3672     {
3673         INT range_start = max(index, format->character_ranges[i].First);
3674         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
3675         if (range_start < range_end)
3676         {
3677             GpRectF range_rect;
3678             SIZE range_size;
3679
3680             range_rect.Y = bounds->Y;
3681             range_rect.Height = bounds->Height;
3682
3683             GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
3684                                   INT_MAX, NULL, NULL, &range_size);
3685             range_rect.X = bounds->X + range_size.cx;
3686
3687             GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
3688                                   INT_MAX, NULL, NULL, &range_size);
3689             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
3690
3691             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
3692             if (stat != Ok)
3693                 break;
3694         }
3695     }
3696
3697     return stat;
3698 }
3699
3700 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
3701         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
3702         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
3703         INT regionCount, GpRegion** regions)
3704 {
3705     GpStatus stat;
3706     int i;
3707     HFONT oldfont;
3708     struct measure_ranges_args args;
3709
3710     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
3711             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
3712
3713     if (!(graphics && string && font && layoutRect && stringFormat && regions))
3714         return InvalidParameter;
3715
3716     if (regionCount < stringFormat->range_count)
3717         return InvalidParameter;
3718
3719     if (stringFormat->attr)
3720         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
3721
3722     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3723
3724     for (i=0; i<stringFormat->range_count; i++)
3725     {
3726         stat = GdipSetEmpty(regions[i]);
3727         if (stat != Ok)
3728             return stat;
3729     }
3730
3731     args.regions = regions;
3732
3733     stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
3734         measure_ranges_callback, &args);
3735
3736     DeleteObject(SelectObject(graphics->hdc, oldfont));
3737
3738     return stat;
3739 }
3740
3741 struct measure_string_args {
3742     RectF *bounds;
3743     INT *codepointsfitted;
3744     INT *linesfilled;
3745 };
3746
3747 static GpStatus measure_string_callback(GpGraphics *graphics,
3748     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3749     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3750     INT lineno, const RectF *bounds, void *user_data)
3751 {
3752     struct measure_string_args *args = user_data;
3753
3754     if (bounds->Width > args->bounds->Width)
3755         args->bounds->Width = bounds->Width;
3756
3757     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
3758         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
3759
3760     if (args->codepointsfitted)
3761         *args->codepointsfitted = index + length;
3762
3763     if (args->linesfilled)
3764         (*args->linesfilled)++;
3765
3766     return Ok;
3767 }
3768
3769 /* Find the smallest rectangle that bounds the text when it is printed in rect
3770  * according to the format options listed in format. If rect has 0 width and
3771  * height, then just find the smallest rectangle that bounds the text when it's
3772  * printed at location (rect->X, rect-Y). */
3773 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
3774     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3775     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
3776     INT *codepointsfitted, INT *linesfilled)
3777 {
3778     HFONT oldfont;
3779     struct measure_string_args args;
3780
3781     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
3782         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
3783         bounds, codepointsfitted, linesfilled);
3784
3785     if(!graphics || !string || !font || !rect || !bounds)
3786         return InvalidParameter;
3787
3788     if(linesfilled) *linesfilled = 0;
3789     if(codepointsfitted) *codepointsfitted = 0;
3790
3791     if(format)
3792         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3793
3794     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3795
3796     bounds->X = rect->X;
3797     bounds->Y = rect->Y;
3798     bounds->Width = 0.0;
3799     bounds->Height = 0.0;
3800
3801     args.bounds = bounds;
3802     args.codepointsfitted = codepointsfitted;
3803     args.linesfilled = linesfilled;
3804
3805     gdip_format_string(graphics, string, length, font, rect, format,
3806         measure_string_callback, &args);
3807
3808     DeleteObject(SelectObject(graphics->hdc, oldfont));
3809
3810     return Ok;
3811 }
3812
3813 struct draw_string_args {
3814     POINT drawbase;
3815     UINT drawflags;
3816     REAL ang_cos, ang_sin;
3817 };
3818
3819 static GpStatus draw_string_callback(GpGraphics *graphics,
3820     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3821     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3822     INT lineno, const RectF *bounds, void *user_data)
3823 {
3824     struct draw_string_args *args = user_data;
3825     RECT drawcoord;
3826
3827     drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
3828     drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
3829
3830     DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
3831
3832     return Ok;
3833 }
3834
3835 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
3836     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
3837     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
3838 {
3839     HRGN rgn = NULL;
3840     HFONT gdifont;
3841     LOGFONTW lfw;
3842     TEXTMETRICW textmet;
3843     GpPointF pt[3], rectcpy[4];
3844     POINT corners[4];
3845     REAL angle, rel_width, rel_height;
3846     INT offsety = 0, save_state;
3847     struct draw_string_args args;
3848     RectF scaled_rect;
3849
3850     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
3851         length, font, debugstr_rectf(rect), format, brush);
3852
3853     if(!graphics || !string || !font || !brush || !rect)
3854         return InvalidParameter;
3855
3856     if((brush->bt != BrushTypeSolidColor)){
3857         FIXME("not implemented for given parameters\n");
3858         return NotImplemented;
3859     }
3860
3861     if(format){
3862         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3863
3864         /* Should be no need to explicitly test for StringAlignmentNear as
3865          * that is default behavior if no alignment is passed. */
3866         if(format->vertalign != StringAlignmentNear){
3867             RectF bounds;
3868             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
3869
3870             if(format->vertalign == StringAlignmentCenter)
3871                 offsety = (rect->Height - bounds.Height) / 2;
3872             else if(format->vertalign == StringAlignmentFar)
3873                 offsety = (rect->Height - bounds.Height);
3874         }
3875     }
3876
3877     save_state = SaveDC(graphics->hdc);
3878     SetBkMode(graphics->hdc, TRANSPARENT);
3879     SetTextColor(graphics->hdc, brush->lb.lbColor);
3880
3881     pt[0].X = 0.0;
3882     pt[0].Y = 0.0;
3883     pt[1].X = 1.0;
3884     pt[1].Y = 0.0;
3885     pt[2].X = 0.0;
3886     pt[2].Y = 1.0;
3887     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
3888     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
3889     args.ang_cos = cos(angle);
3890     args.ang_sin = sin(angle);
3891     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
3892                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
3893     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
3894                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
3895
3896     rectcpy[3].X = rectcpy[0].X = rect->X;
3897     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
3898     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
3899     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
3900     transform_and_round_points(graphics, corners, rectcpy, 4);
3901
3902     scaled_rect.X = 0.0;
3903     scaled_rect.Y = 0.0;
3904     scaled_rect.Width = rel_width * rect->Width;
3905     scaled_rect.Height = rel_height * rect->Height;
3906
3907     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
3908     {
3909         /* FIXME: If only the width or only the height is 0, we should probably still clip */
3910         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
3911         SelectClipRgn(graphics->hdc, rgn);
3912     }
3913
3914     /* Use gdi to find the font, then perform transformations on it (height,
3915      * width, angle). */
3916     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3917     GetTextMetricsW(graphics->hdc, &textmet);
3918     lfw = font->lfw;
3919
3920     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
3921     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
3922
3923     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
3924
3925     gdifont = CreateFontIndirectW(&lfw);
3926     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
3927
3928     if (!format || format->align == StringAlignmentNear)
3929     {
3930         args.drawbase.x = corners[0].x;
3931         args.drawbase.y = corners[0].y;
3932         args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
3933     }
3934     else if (format->align == StringAlignmentCenter)
3935     {
3936         args.drawbase.x = (corners[0].x + corners[1].x)/2;
3937         args.drawbase.y = (corners[0].y + corners[1].y)/2;
3938         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
3939     }
3940     else /* (format->align == StringAlignmentFar) */
3941     {
3942         args.drawbase.x = corners[1].x;
3943         args.drawbase.y = corners[1].y;
3944         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
3945     }
3946
3947     gdip_format_string(graphics, string, length, font, &scaled_rect, format,
3948         draw_string_callback, &args);
3949
3950     DeleteObject(rgn);
3951     DeleteObject(gdifont);
3952
3953     RestoreDC(graphics->hdc, save_state);
3954
3955     return Ok;
3956 }
3957
3958 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3959 {
3960     TRACE("(%p)\n", graphics);
3961
3962     if(!graphics)
3963         return InvalidParameter;
3964
3965     if(graphics->busy)
3966         return ObjectBusy;
3967
3968     return GdipSetInfinite(graphics->clip);
3969 }
3970
3971 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3972 {
3973     TRACE("(%p)\n", graphics);
3974
3975     if(!graphics)
3976         return InvalidParameter;
3977
3978     if(graphics->busy)
3979         return ObjectBusy;
3980
3981     graphics->worldtrans->matrix[0] = 1.0;
3982     graphics->worldtrans->matrix[1] = 0.0;
3983     graphics->worldtrans->matrix[2] = 0.0;
3984     graphics->worldtrans->matrix[3] = 1.0;
3985     graphics->worldtrans->matrix[4] = 0.0;
3986     graphics->worldtrans->matrix[5] = 0.0;
3987
3988     return Ok;
3989 }
3990
3991 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3992 {
3993     return GdipEndContainer(graphics, state);
3994 }
3995
3996 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3997     GpMatrixOrder order)
3998 {
3999     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
4000
4001     if(!graphics)
4002         return InvalidParameter;
4003
4004     if(graphics->busy)
4005         return ObjectBusy;
4006
4007     return GdipRotateMatrix(graphics->worldtrans, angle, order);
4008 }
4009
4010 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
4011 {
4012     return GdipBeginContainer2(graphics, state);
4013 }
4014
4015 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
4016         GraphicsContainer *state)
4017 {
4018     GraphicsContainerItem *container;
4019     GpStatus sts;
4020
4021     TRACE("(%p, %p)\n", graphics, state);
4022
4023     if(!graphics || !state)
4024         return InvalidParameter;
4025
4026     sts = init_container(&container, graphics);
4027     if(sts != Ok)
4028         return sts;
4029
4030     list_add_head(&graphics->containers, &container->entry);
4031     *state = graphics->contid = container->contid;
4032
4033     return Ok;
4034 }
4035
4036 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
4037 {
4038     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4039     return NotImplemented;
4040 }
4041
4042 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
4043 {
4044     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4045     return NotImplemented;
4046 }
4047
4048 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4049 {
4050     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4051     return NotImplemented;
4052 }
4053
4054 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4055 {
4056     GpStatus sts;
4057     GraphicsContainerItem *container, *container2;
4058
4059     TRACE("(%p, %x)\n", graphics, state);
4060
4061     if(!graphics)
4062         return InvalidParameter;
4063
4064     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4065         if(container->contid == state)
4066             break;
4067     }
4068
4069     /* did not find a matching container */
4070     if(&container->entry == &graphics->containers)
4071         return Ok;
4072
4073     sts = restore_container(graphics, container);
4074     if(sts != Ok)
4075         return sts;
4076
4077     /* remove all of the containers on top of the found container */
4078     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4079         if(container->contid == state)
4080             break;
4081         list_remove(&container->entry);
4082         delete_container(container);
4083     }
4084
4085     list_remove(&container->entry);
4086     delete_container(container);
4087
4088     return Ok;
4089 }
4090
4091 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4092     REAL sy, GpMatrixOrder order)
4093 {
4094     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4095
4096     if(!graphics)
4097         return InvalidParameter;
4098
4099     if(graphics->busy)
4100         return ObjectBusy;
4101
4102     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4103 }
4104
4105 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4106     CombineMode mode)
4107 {
4108     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4109
4110     if(!graphics || !srcgraphics)
4111         return InvalidParameter;
4112
4113     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4114 }
4115
4116 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4117     CompositingMode mode)
4118 {
4119     TRACE("(%p, %d)\n", graphics, mode);
4120
4121     if(!graphics)
4122         return InvalidParameter;
4123
4124     if(graphics->busy)
4125         return ObjectBusy;
4126
4127     graphics->compmode = mode;
4128
4129     return Ok;
4130 }
4131
4132 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4133     CompositingQuality quality)
4134 {
4135     TRACE("(%p, %d)\n", graphics, quality);
4136
4137     if(!graphics)
4138         return InvalidParameter;
4139
4140     if(graphics->busy)
4141         return ObjectBusy;
4142
4143     graphics->compqual = quality;
4144
4145     return Ok;
4146 }
4147
4148 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4149     InterpolationMode mode)
4150 {
4151     TRACE("(%p, %d)\n", graphics, mode);
4152
4153     if(!graphics)
4154         return InvalidParameter;
4155
4156     if(graphics->busy)
4157         return ObjectBusy;
4158
4159     graphics->interpolation = mode;
4160
4161     return Ok;
4162 }
4163
4164 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4165 {
4166     TRACE("(%p, %.2f)\n", graphics, scale);
4167
4168     if(!graphics || (scale <= 0.0))
4169         return InvalidParameter;
4170
4171     if(graphics->busy)
4172         return ObjectBusy;
4173
4174     graphics->scale = scale;
4175
4176     return Ok;
4177 }
4178
4179 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4180 {
4181     TRACE("(%p, %d)\n", graphics, unit);
4182
4183     if(!graphics)
4184         return InvalidParameter;
4185
4186     if(graphics->busy)
4187         return ObjectBusy;
4188
4189     if(unit == UnitWorld)
4190         return InvalidParameter;
4191
4192     graphics->unit = unit;
4193
4194     return Ok;
4195 }
4196
4197 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4198     mode)
4199 {
4200     TRACE("(%p, %d)\n", graphics, mode);
4201
4202     if(!graphics)
4203         return InvalidParameter;
4204
4205     if(graphics->busy)
4206         return ObjectBusy;
4207
4208     graphics->pixeloffset = mode;
4209
4210     return Ok;
4211 }
4212
4213 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
4214 {
4215     static int calls;
4216
4217     TRACE("(%p,%i,%i)\n", graphics, x, y);
4218
4219     if (!(calls++))
4220         FIXME("not implemented\n");
4221
4222     return NotImplemented;
4223 }
4224
4225 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
4226 {
4227     static int calls;
4228
4229     TRACE("(%p,%p,%p)\n", graphics, x, y);
4230
4231     if (!(calls++))
4232         FIXME("not implemented\n");
4233
4234     *x = *y = 0;
4235
4236     return NotImplemented;
4237 }
4238
4239 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
4240 {
4241     TRACE("(%p, %d)\n", graphics, mode);
4242
4243     if(!graphics)
4244         return InvalidParameter;
4245
4246     if(graphics->busy)
4247         return ObjectBusy;
4248
4249     graphics->smoothing = mode;
4250
4251     return Ok;
4252 }
4253
4254 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
4255 {
4256     TRACE("(%p, %d)\n", graphics, contrast);
4257
4258     if(!graphics)
4259         return InvalidParameter;
4260
4261     graphics->textcontrast = contrast;
4262
4263     return Ok;
4264 }
4265
4266 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
4267     TextRenderingHint hint)
4268 {
4269     TRACE("(%p, %d)\n", graphics, hint);
4270
4271     if(!graphics)
4272         return InvalidParameter;
4273
4274     if(graphics->busy)
4275         return ObjectBusy;
4276
4277     graphics->texthint = hint;
4278
4279     return Ok;
4280 }
4281
4282 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4283 {
4284     TRACE("(%p, %p)\n", graphics, matrix);
4285
4286     if(!graphics || !matrix)
4287         return InvalidParameter;
4288
4289     if(graphics->busy)
4290         return ObjectBusy;
4291
4292     GdipDeleteMatrix(graphics->worldtrans);
4293     return GdipCloneMatrix(matrix, &graphics->worldtrans);
4294 }
4295
4296 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
4297     REAL dy, GpMatrixOrder order)
4298 {
4299     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
4300
4301     if(!graphics)
4302         return InvalidParameter;
4303
4304     if(graphics->busy)
4305         return ObjectBusy;
4306
4307     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
4308 }
4309
4310 /*****************************************************************************
4311  * GdipSetClipHrgn [GDIPLUS.@]
4312  */
4313 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
4314 {
4315     GpRegion *region;
4316     GpStatus status;
4317
4318     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
4319
4320     if(!graphics)
4321         return InvalidParameter;
4322
4323     status = GdipCreateRegionHrgn(hrgn, &region);
4324     if(status != Ok)
4325         return status;
4326
4327     status = GdipSetClipRegion(graphics, region, mode);
4328
4329     GdipDeleteRegion(region);
4330     return status;
4331 }
4332
4333 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
4334 {
4335     TRACE("(%p, %p, %d)\n", graphics, path, mode);
4336
4337     if(!graphics)
4338         return InvalidParameter;
4339
4340     if(graphics->busy)
4341         return ObjectBusy;
4342
4343     return GdipCombineRegionPath(graphics->clip, path, mode);
4344 }
4345
4346 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
4347                                     REAL width, REAL height,
4348                                     CombineMode mode)
4349 {
4350     GpRectF rect;
4351
4352     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
4353
4354     if(!graphics)
4355         return InvalidParameter;
4356
4357     if(graphics->busy)
4358         return ObjectBusy;
4359
4360     rect.X = x;
4361     rect.Y = y;
4362     rect.Width  = width;
4363     rect.Height = height;
4364
4365     return GdipCombineRegionRect(graphics->clip, &rect, mode);
4366 }
4367
4368 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
4369                                      INT width, INT height,
4370                                      CombineMode mode)
4371 {
4372     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
4373
4374     if(!graphics)
4375         return InvalidParameter;
4376
4377     if(graphics->busy)
4378         return ObjectBusy;
4379
4380     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
4381 }
4382
4383 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
4384                                       CombineMode mode)
4385 {
4386     TRACE("(%p, %p, %d)\n", graphics, region, mode);
4387
4388     if(!graphics || !region)
4389         return InvalidParameter;
4390
4391     if(graphics->busy)
4392         return ObjectBusy;
4393
4394     return GdipCombineRegionRegion(graphics->clip, region, mode);
4395 }
4396
4397 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4398     UINT limitDpi)
4399 {
4400     static int calls;
4401
4402     TRACE("(%p,%u)\n", metafile, limitDpi);
4403
4404     if(!(calls++))
4405         FIXME("not implemented\n");
4406
4407     return NotImplemented;
4408 }
4409
4410 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
4411     INT count)
4412 {
4413     INT save_state;
4414     POINT *pti;
4415
4416     TRACE("(%p, %p, %d)\n", graphics, points, count);
4417
4418     if(!graphics || !pen || count<=0)
4419         return InvalidParameter;
4420
4421     if(graphics->busy)
4422         return ObjectBusy;
4423
4424     pti = GdipAlloc(sizeof(POINT) * count);
4425
4426     save_state = prepare_dc(graphics, pen);
4427     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
4428
4429     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
4430     Polygon(graphics->hdc, pti, count);
4431
4432     restore_dc(graphics, save_state);
4433     GdipFree(pti);
4434
4435     return Ok;
4436 }
4437
4438 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
4439     INT count)
4440 {
4441     GpStatus ret;
4442     GpPointF *ptf;
4443     INT i;
4444
4445     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
4446
4447     if(count<=0)    return InvalidParameter;
4448     ptf = GdipAlloc(sizeof(GpPointF) * count);
4449
4450     for(i = 0;i < count; i++){
4451         ptf[i].X = (REAL)points[i].X;
4452         ptf[i].Y = (REAL)points[i].Y;
4453     }
4454
4455     ret = GdipDrawPolygon(graphics,pen,ptf,count);
4456     GdipFree(ptf);
4457
4458     return ret;
4459 }
4460
4461 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
4462 {
4463     TRACE("(%p, %p)\n", graphics, dpi);
4464
4465     if(!graphics || !dpi)
4466         return InvalidParameter;
4467
4468     if(graphics->busy)
4469         return ObjectBusy;
4470
4471     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
4472
4473     return Ok;
4474 }
4475
4476 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
4477 {
4478     TRACE("(%p, %p)\n", graphics, dpi);
4479
4480     if(!graphics || !dpi)
4481         return InvalidParameter;
4482
4483     if(graphics->busy)
4484         return ObjectBusy;
4485
4486     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
4487
4488     return Ok;
4489 }
4490
4491 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
4492     GpMatrixOrder order)
4493 {
4494     GpMatrix m;
4495     GpStatus ret;
4496
4497     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
4498
4499     if(!graphics || !matrix)
4500         return InvalidParameter;
4501
4502     if(graphics->busy)
4503         return ObjectBusy;
4504
4505     m = *(graphics->worldtrans);
4506
4507     ret = GdipMultiplyMatrix(&m, matrix, order);
4508     if(ret == Ok)
4509         *(graphics->worldtrans) = m;
4510
4511     return ret;
4512 }
4513
4514 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
4515 {
4516     TRACE("(%p, %p)\n", graphics, hdc);
4517
4518     if(!graphics || !hdc)
4519         return InvalidParameter;
4520
4521     if(graphics->busy)
4522         return ObjectBusy;
4523
4524     *hdc = graphics->hdc;
4525     graphics->busy = TRUE;
4526
4527     return Ok;
4528 }
4529
4530 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
4531 {
4532     TRACE("(%p, %p)\n", graphics, hdc);
4533
4534     if(!graphics)
4535         return InvalidParameter;
4536
4537     if(graphics->hdc != hdc || !(graphics->busy))
4538         return InvalidParameter;
4539
4540     graphics->busy = FALSE;
4541
4542     return Ok;
4543 }
4544
4545 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
4546 {
4547     GpRegion *clip;
4548     GpStatus status;
4549
4550     TRACE("(%p, %p)\n", graphics, region);
4551
4552     if(!graphics || !region)
4553         return InvalidParameter;
4554
4555     if(graphics->busy)
4556         return ObjectBusy;
4557
4558     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
4559         return status;
4560
4561     /* free everything except root node and header */
4562     delete_element(&region->node);
4563     memcpy(region, clip, sizeof(GpRegion));
4564     GdipFree(clip);
4565
4566     return Ok;
4567 }
4568
4569 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
4570                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
4571 {
4572     GpMatrix *matrix;
4573     GpStatus stat;
4574     REAL unitscale;
4575
4576     if(!graphics || !points || count <= 0)
4577         return InvalidParameter;
4578
4579     if(graphics->busy)
4580         return ObjectBusy;
4581
4582     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4583
4584     if (src_space == dst_space) return Ok;
4585
4586     stat = GdipCreateMatrix(&matrix);
4587     if (stat == Ok)
4588     {
4589         unitscale = convert_unit(graphics->hdc, graphics->unit);
4590
4591         if(graphics->unit != UnitDisplay)
4592             unitscale *= graphics->scale;
4593
4594         /* transform from src_space to CoordinateSpacePage */
4595         switch (src_space)
4596         {
4597         case CoordinateSpaceWorld:
4598             GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
4599             break;
4600         case CoordinateSpacePage:
4601             break;
4602         case CoordinateSpaceDevice:
4603             GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
4604             break;
4605         }
4606
4607         /* transform from CoordinateSpacePage to dst_space */
4608         switch (dst_space)
4609         {
4610         case CoordinateSpaceWorld:
4611             {
4612                 GpMatrix *inverted_transform;
4613                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
4614                 if (stat == Ok)
4615                 {
4616                     stat = GdipInvertMatrix(inverted_transform);
4617                     if (stat == Ok)
4618                         GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
4619                     GdipDeleteMatrix(inverted_transform);
4620                 }
4621                 break;
4622             }
4623         case CoordinateSpacePage:
4624             break;
4625         case CoordinateSpaceDevice:
4626             GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
4627             break;
4628         }
4629
4630         if (stat == Ok)
4631             stat = GdipTransformMatrixPoints(matrix, points, count);
4632
4633         GdipDeleteMatrix(matrix);
4634     }
4635
4636     return stat;
4637 }
4638
4639 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
4640                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
4641 {
4642     GpPointF *pointsF;
4643     GpStatus ret;
4644     INT i;
4645
4646     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4647
4648     if(count <= 0)
4649         return InvalidParameter;
4650
4651     pointsF = GdipAlloc(sizeof(GpPointF) * count);
4652     if(!pointsF)
4653         return OutOfMemory;
4654
4655     for(i = 0; i < count; i++){
4656         pointsF[i].X = (REAL)points[i].X;
4657         pointsF[i].Y = (REAL)points[i].Y;
4658     }
4659
4660     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
4661
4662     if(ret == Ok)
4663         for(i = 0; i < count; i++){
4664             points[i].X = roundr(pointsF[i].X);
4665             points[i].Y = roundr(pointsF[i].Y);
4666         }
4667     GdipFree(pointsF);
4668
4669     return ret;
4670 }
4671
4672 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
4673 {
4674     static int calls;
4675
4676     TRACE("\n");
4677
4678     if (!calls++)
4679       FIXME("stub\n");
4680
4681     return NULL;
4682 }
4683
4684 /*****************************************************************************
4685  * GdipTranslateClip [GDIPLUS.@]
4686  */
4687 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
4688 {
4689     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
4690
4691     if(!graphics)
4692         return InvalidParameter;
4693
4694     if(graphics->busy)
4695         return ObjectBusy;
4696
4697     return GdipTranslateRegion(graphics->clip, dx, dy);
4698 }
4699
4700 /*****************************************************************************
4701  * GdipTranslateClipI [GDIPLUS.@]
4702  */
4703 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
4704 {
4705     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
4706
4707     if(!graphics)
4708         return InvalidParameter;
4709
4710     if(graphics->busy)
4711         return ObjectBusy;
4712
4713     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
4714 }
4715
4716
4717 /*****************************************************************************
4718  * GdipMeasureDriverString [GDIPLUS.@]
4719  */
4720 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4721                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
4722                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
4723 {
4724     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
4725     return NotImplemented;
4726 }
4727
4728 /*****************************************************************************
4729  * GdipDrawDriverString [GDIPLUS.@]
4730  */
4731 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4732                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
4733                                          GDIPCONST PointF *positions, INT flags,
4734                                          GDIPCONST GpMatrix *matrix )
4735 {
4736     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
4737     return NotImplemented;
4738 }
4739
4740 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
4741                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4742 {
4743     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4744     return NotImplemented;
4745 }
4746
4747 /*****************************************************************************
4748  * GdipRecordMetafileI [GDIPLUS.@]
4749  */
4750 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4751                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4752 {
4753     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4754     return NotImplemented;
4755 }
4756
4757 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4758                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4759 {
4760     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
4761     return NotImplemented;
4762 }
4763
4764 /*****************************************************************************
4765  * GdipIsVisibleClipEmpty [GDIPLUS.@]
4766  */
4767 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
4768 {
4769     GpStatus stat;
4770     GpRegion* rgn;
4771
4772     TRACE("(%p, %p)\n", graphics, res);
4773
4774     if((stat = GdipCreateRegion(&rgn)) != Ok)
4775         return stat;
4776
4777     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4778         goto cleanup;
4779
4780     stat = GdipIsEmptyRegion(rgn, graphics, res);
4781
4782 cleanup:
4783     GdipDeleteRegion(rgn);
4784     return stat;
4785 }