gdiplus: Stub GdipRecordMetafileStream.
[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     stat = GdipCreatePath(fill, &path);
2677     if(stat != Ok)
2678         return stat;
2679
2680     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2681     if(stat != Ok){
2682         GdipDeletePath(path);
2683         return stat;
2684     }
2685
2686     stat = GdipFillPath(graphics, brush, path);
2687     if(stat != Ok){
2688         GdipDeletePath(path);
2689         return stat;
2690     }
2691
2692     GdipDeletePath(path);
2693
2694     return Ok;
2695 }
2696
2697 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2698     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2699 {
2700     GpPointF *ptf;
2701     GpStatus stat;
2702     INT i;
2703
2704     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2705             count, tension, fill);
2706
2707     if(!points || count <= 0)
2708         return InvalidParameter;
2709
2710     ptf = GdipAlloc(sizeof(GpPointF)*count);
2711     if(!ptf)
2712         return OutOfMemory;
2713
2714     for(i = 0;i < count;i++){
2715         ptf[i].X = (REAL)points[i].X;
2716         ptf[i].Y = (REAL)points[i].Y;
2717     }
2718
2719     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2720
2721     GdipFree(ptf);
2722
2723     return stat;
2724 }
2725
2726 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2727     REAL y, REAL width, REAL height)
2728 {
2729     INT save_state;
2730     GpPointF ptf[2];
2731     POINT pti[2];
2732
2733     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2734
2735     if(!graphics || !brush)
2736         return InvalidParameter;
2737
2738     if(graphics->busy)
2739         return ObjectBusy;
2740
2741     ptf[0].X = x;
2742     ptf[0].Y = y;
2743     ptf[1].X = x + width;
2744     ptf[1].Y = y + height;
2745
2746     save_state = SaveDC(graphics->hdc);
2747     EndPath(graphics->hdc);
2748
2749     transform_and_round_points(graphics, pti, ptf, 2);
2750
2751     BeginPath(graphics->hdc);
2752     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2753     EndPath(graphics->hdc);
2754
2755     brush_fill_path(graphics, brush);
2756
2757     RestoreDC(graphics->hdc, save_state);
2758
2759     return Ok;
2760 }
2761
2762 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2763     INT y, INT width, INT height)
2764 {
2765     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2766
2767     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2768 }
2769
2770 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2771 {
2772     INT save_state;
2773     GpStatus retval;
2774
2775     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2776
2777     if(!brush || !graphics || !path)
2778         return InvalidParameter;
2779
2780     if(graphics->busy)
2781         return ObjectBusy;
2782
2783     save_state = SaveDC(graphics->hdc);
2784     EndPath(graphics->hdc);
2785     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2786                                                                     : WINDING));
2787
2788     BeginPath(graphics->hdc);
2789     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2790                        path->pathdata.Types, path->pathdata.Count, FALSE);
2791
2792     if(retval != Ok)
2793         goto end;
2794
2795     EndPath(graphics->hdc);
2796     brush_fill_path(graphics, brush);
2797
2798     retval = Ok;
2799
2800 end:
2801     RestoreDC(graphics->hdc, save_state);
2802
2803     return retval;
2804 }
2805
2806 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2807     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2808 {
2809     INT save_state;
2810
2811     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2812             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2813
2814     if(!graphics || !brush)
2815         return InvalidParameter;
2816
2817     if(graphics->busy)
2818         return ObjectBusy;
2819
2820     save_state = SaveDC(graphics->hdc);
2821     EndPath(graphics->hdc);
2822
2823     BeginPath(graphics->hdc);
2824     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2825     EndPath(graphics->hdc);
2826
2827     brush_fill_path(graphics, brush);
2828
2829     RestoreDC(graphics->hdc, save_state);
2830
2831     return Ok;
2832 }
2833
2834 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2835     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2836 {
2837     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2838             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2839
2840     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2841 }
2842
2843 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2844     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2845 {
2846     INT save_state;
2847     GpPointF *ptf = NULL;
2848     POINT *pti = NULL;
2849     GpStatus retval = Ok;
2850
2851     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2852
2853     if(!graphics || !brush || !points || !count)
2854         return InvalidParameter;
2855
2856     if(graphics->busy)
2857         return ObjectBusy;
2858
2859     ptf = GdipAlloc(count * sizeof(GpPointF));
2860     pti = GdipAlloc(count * sizeof(POINT));
2861     if(!ptf || !pti){
2862         retval = OutOfMemory;
2863         goto end;
2864     }
2865
2866     memcpy(ptf, points, count * sizeof(GpPointF));
2867
2868     save_state = SaveDC(graphics->hdc);
2869     EndPath(graphics->hdc);
2870     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2871                                                                   : WINDING));
2872
2873     transform_and_round_points(graphics, pti, ptf, count);
2874
2875     BeginPath(graphics->hdc);
2876     Polygon(graphics->hdc, pti, count);
2877     EndPath(graphics->hdc);
2878
2879     brush_fill_path(graphics, brush);
2880
2881     RestoreDC(graphics->hdc, save_state);
2882
2883 end:
2884     GdipFree(ptf);
2885     GdipFree(pti);
2886
2887     return retval;
2888 }
2889
2890 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2891     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2892 {
2893     INT save_state, i;
2894     GpPointF *ptf = NULL;
2895     POINT *pti = NULL;
2896     GpStatus retval = Ok;
2897
2898     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2899
2900     if(!graphics || !brush || !points || !count)
2901         return InvalidParameter;
2902
2903     if(graphics->busy)
2904         return ObjectBusy;
2905
2906     ptf = GdipAlloc(count * sizeof(GpPointF));
2907     pti = GdipAlloc(count * sizeof(POINT));
2908     if(!ptf || !pti){
2909         retval = OutOfMemory;
2910         goto end;
2911     }
2912
2913     for(i = 0; i < count; i ++){
2914         ptf[i].X = (REAL) points[i].X;
2915         ptf[i].Y = (REAL) points[i].Y;
2916     }
2917
2918     save_state = SaveDC(graphics->hdc);
2919     EndPath(graphics->hdc);
2920     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2921                                                                   : WINDING));
2922
2923     transform_and_round_points(graphics, pti, ptf, count);
2924
2925     BeginPath(graphics->hdc);
2926     Polygon(graphics->hdc, pti, count);
2927     EndPath(graphics->hdc);
2928
2929     brush_fill_path(graphics, brush);
2930
2931     RestoreDC(graphics->hdc, save_state);
2932
2933 end:
2934     GdipFree(ptf);
2935     GdipFree(pti);
2936
2937     return retval;
2938 }
2939
2940 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2941     GDIPCONST GpPointF *points, INT count)
2942 {
2943     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2944
2945     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2946 }
2947
2948 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2949     GDIPCONST GpPoint *points, INT count)
2950 {
2951     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2952
2953     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2954 }
2955
2956 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2957     REAL x, REAL y, REAL width, REAL height)
2958 {
2959     INT save_state;
2960     GpPointF ptf[4];
2961     POINT pti[4];
2962
2963     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2964
2965     if(!graphics || !brush)
2966         return InvalidParameter;
2967
2968     if(graphics->busy)
2969         return ObjectBusy;
2970
2971     ptf[0].X = x;
2972     ptf[0].Y = y;
2973     ptf[1].X = x + width;
2974     ptf[1].Y = y;
2975     ptf[2].X = x + width;
2976     ptf[2].Y = y + height;
2977     ptf[3].X = x;
2978     ptf[3].Y = y + height;
2979
2980     save_state = SaveDC(graphics->hdc);
2981     EndPath(graphics->hdc);
2982
2983     transform_and_round_points(graphics, pti, ptf, 4);
2984
2985     BeginPath(graphics->hdc);
2986     Polygon(graphics->hdc, pti, 4);
2987     EndPath(graphics->hdc);
2988
2989     brush_fill_path(graphics, brush);
2990
2991     RestoreDC(graphics->hdc, save_state);
2992
2993     return Ok;
2994 }
2995
2996 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2997     INT x, INT y, INT width, INT height)
2998 {
2999     INT save_state;
3000     GpPointF ptf[4];
3001     POINT pti[4];
3002
3003     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3004
3005     if(!graphics || !brush)
3006         return InvalidParameter;
3007
3008     if(graphics->busy)
3009         return ObjectBusy;
3010
3011     ptf[0].X = x;
3012     ptf[0].Y = y;
3013     ptf[1].X = x + width;
3014     ptf[1].Y = y;
3015     ptf[2].X = x + width;
3016     ptf[2].Y = y + height;
3017     ptf[3].X = x;
3018     ptf[3].Y = y + height;
3019
3020     save_state = SaveDC(graphics->hdc);
3021     EndPath(graphics->hdc);
3022
3023     transform_and_round_points(graphics, pti, ptf, 4);
3024
3025     BeginPath(graphics->hdc);
3026     Polygon(graphics->hdc, pti, 4);
3027     EndPath(graphics->hdc);
3028
3029     brush_fill_path(graphics, brush);
3030
3031     RestoreDC(graphics->hdc, save_state);
3032
3033     return Ok;
3034 }
3035
3036 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3037     INT count)
3038 {
3039     GpStatus ret;
3040     INT i;
3041
3042     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3043
3044     if(!rects)
3045         return InvalidParameter;
3046
3047     for(i = 0; i < count; i++){
3048         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3049         if(ret != Ok)   return ret;
3050     }
3051
3052     return Ok;
3053 }
3054
3055 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3056     INT count)
3057 {
3058     GpRectF *rectsF;
3059     GpStatus ret;
3060     INT i;
3061
3062     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3063
3064     if(!rects || count <= 0)
3065         return InvalidParameter;
3066
3067     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3068     if(!rectsF)
3069         return OutOfMemory;
3070
3071     for(i = 0; i < count; i++){
3072         rectsF[i].X      = (REAL)rects[i].X;
3073         rectsF[i].Y      = (REAL)rects[i].Y;
3074         rectsF[i].X      = (REAL)rects[i].Width;
3075         rectsF[i].Height = (REAL)rects[i].Height;
3076     }
3077
3078     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3079     GdipFree(rectsF);
3080
3081     return ret;
3082 }
3083
3084 /*****************************************************************************
3085  * GdipFillRegion [GDIPLUS.@]
3086  */
3087 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3088         GpRegion* region)
3089 {
3090     INT save_state;
3091     GpStatus status;
3092     HRGN hrgn;
3093     RECT rc;
3094
3095     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3096
3097     if (!(graphics && brush && region))
3098         return InvalidParameter;
3099
3100     if(graphics->busy)
3101         return ObjectBusy;
3102
3103     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3104     if(status != Ok)
3105         return status;
3106
3107     save_state = SaveDC(graphics->hdc);
3108     EndPath(graphics->hdc);
3109
3110     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3111
3112     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3113     {
3114         BeginPath(graphics->hdc);
3115         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3116         EndPath(graphics->hdc);
3117
3118         brush_fill_path(graphics, brush);
3119     }
3120
3121     RestoreDC(graphics->hdc, save_state);
3122
3123     DeleteObject(hrgn);
3124
3125     return Ok;
3126 }
3127
3128 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3129 {
3130     TRACE("(%p,%u)\n", graphics, intention);
3131
3132     if(!graphics)
3133         return InvalidParameter;
3134
3135     if(graphics->busy)
3136         return ObjectBusy;
3137
3138     /* We have no internal operation queue, so there's no need to clear it. */
3139
3140     if (graphics->hdc)
3141         GdiFlush();
3142
3143     return Ok;
3144 }
3145
3146 /*****************************************************************************
3147  * GdipGetClipBounds [GDIPLUS.@]
3148  */
3149 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3150 {
3151     TRACE("(%p, %p)\n", graphics, rect);
3152
3153     if(!graphics)
3154         return InvalidParameter;
3155
3156     if(graphics->busy)
3157         return ObjectBusy;
3158
3159     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3160 }
3161
3162 /*****************************************************************************
3163  * GdipGetClipBoundsI [GDIPLUS.@]
3164  */
3165 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3166 {
3167     TRACE("(%p, %p)\n", graphics, rect);
3168
3169     if(!graphics)
3170         return InvalidParameter;
3171
3172     if(graphics->busy)
3173         return ObjectBusy;
3174
3175     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3176 }
3177
3178 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3179 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3180     CompositingMode *mode)
3181 {
3182     TRACE("(%p, %p)\n", graphics, mode);
3183
3184     if(!graphics || !mode)
3185         return InvalidParameter;
3186
3187     if(graphics->busy)
3188         return ObjectBusy;
3189
3190     *mode = graphics->compmode;
3191
3192     return Ok;
3193 }
3194
3195 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3196 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3197     CompositingQuality *quality)
3198 {
3199     TRACE("(%p, %p)\n", graphics, quality);
3200
3201     if(!graphics || !quality)
3202         return InvalidParameter;
3203
3204     if(graphics->busy)
3205         return ObjectBusy;
3206
3207     *quality = graphics->compqual;
3208
3209     return Ok;
3210 }
3211
3212 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
3213 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
3214     InterpolationMode *mode)
3215 {
3216     TRACE("(%p, %p)\n", graphics, mode);
3217
3218     if(!graphics || !mode)
3219         return InvalidParameter;
3220
3221     if(graphics->busy)
3222         return ObjectBusy;
3223
3224     *mode = graphics->interpolation;
3225
3226     return Ok;
3227 }
3228
3229 /* FIXME: Need to handle color depths less than 24bpp */
3230 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
3231 {
3232     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
3233
3234     if(!graphics || !argb)
3235         return InvalidParameter;
3236
3237     if(graphics->busy)
3238         return ObjectBusy;
3239
3240     return Ok;
3241 }
3242
3243 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
3244 {
3245     TRACE("(%p, %p)\n", graphics, scale);
3246
3247     if(!graphics || !scale)
3248         return InvalidParameter;
3249
3250     if(graphics->busy)
3251         return ObjectBusy;
3252
3253     *scale = graphics->scale;
3254
3255     return Ok;
3256 }
3257
3258 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
3259 {
3260     TRACE("(%p, %p)\n", graphics, unit);
3261
3262     if(!graphics || !unit)
3263         return InvalidParameter;
3264
3265     if(graphics->busy)
3266         return ObjectBusy;
3267
3268     *unit = graphics->unit;
3269
3270     return Ok;
3271 }
3272
3273 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
3274 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3275     *mode)
3276 {
3277     TRACE("(%p, %p)\n", graphics, mode);
3278
3279     if(!graphics || !mode)
3280         return InvalidParameter;
3281
3282     if(graphics->busy)
3283         return ObjectBusy;
3284
3285     *mode = graphics->pixeloffset;
3286
3287     return Ok;
3288 }
3289
3290 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
3291 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
3292 {
3293     TRACE("(%p, %p)\n", graphics, mode);
3294
3295     if(!graphics || !mode)
3296         return InvalidParameter;
3297
3298     if(graphics->busy)
3299         return ObjectBusy;
3300
3301     *mode = graphics->smoothing;
3302
3303     return Ok;
3304 }
3305
3306 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
3307 {
3308     TRACE("(%p, %p)\n", graphics, contrast);
3309
3310     if(!graphics || !contrast)
3311         return InvalidParameter;
3312
3313     *contrast = graphics->textcontrast;
3314
3315     return Ok;
3316 }
3317
3318 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
3319 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
3320     TextRenderingHint *hint)
3321 {
3322     TRACE("(%p, %p)\n", graphics, hint);
3323
3324     if(!graphics || !hint)
3325         return InvalidParameter;
3326
3327     if(graphics->busy)
3328         return ObjectBusy;
3329
3330     *hint = graphics->texthint;
3331
3332     return Ok;
3333 }
3334
3335 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
3336 {
3337     GpRegion *clip_rgn;
3338     GpStatus stat;
3339
3340     TRACE("(%p, %p)\n", graphics, rect);
3341
3342     if(!graphics || !rect)
3343         return InvalidParameter;
3344
3345     if(graphics->busy)
3346         return ObjectBusy;
3347
3348     /* intersect window and graphics clipping regions */
3349     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
3350         return stat;
3351
3352     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
3353         goto cleanup;
3354
3355     /* get bounds of the region */
3356     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
3357
3358 cleanup:
3359     GdipDeleteRegion(clip_rgn);
3360
3361     return stat;
3362 }
3363
3364 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3365 {
3366     GpRectF rectf;
3367     GpStatus stat;
3368
3369     TRACE("(%p, %p)\n", graphics, rect);
3370
3371     if(!graphics || !rect)
3372         return InvalidParameter;
3373
3374     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
3375     {
3376         rect->X = roundr(rectf.X);
3377         rect->Y = roundr(rectf.Y);
3378         rect->Width  = roundr(rectf.Width);
3379         rect->Height = roundr(rectf.Height);
3380     }
3381
3382     return stat;
3383 }
3384
3385 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3386 {
3387     TRACE("(%p, %p)\n", graphics, matrix);
3388
3389     if(!graphics || !matrix)
3390         return InvalidParameter;
3391
3392     if(graphics->busy)
3393         return ObjectBusy;
3394
3395     *matrix = *graphics->worldtrans;
3396     return Ok;
3397 }
3398
3399 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
3400 {
3401     GpSolidFill *brush;
3402     GpStatus stat;
3403     GpRectF wnd_rect;
3404
3405     TRACE("(%p, %x)\n", graphics, color);
3406
3407     if(!graphics)
3408         return InvalidParameter;
3409
3410     if(graphics->busy)
3411         return ObjectBusy;
3412
3413     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3414         return stat;
3415
3416     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
3417         GdipDeleteBrush((GpBrush*)brush);
3418         return stat;
3419     }
3420
3421     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
3422                                                  wnd_rect.Width, wnd_rect.Height);
3423
3424     GdipDeleteBrush((GpBrush*)brush);
3425
3426     return Ok;
3427 }
3428
3429 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3430 {
3431     TRACE("(%p, %p)\n", graphics, res);
3432
3433     if(!graphics || !res)
3434         return InvalidParameter;
3435
3436     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3437 }
3438
3439 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3440 {
3441     GpStatus stat;
3442     GpRegion* rgn;
3443     GpPointF pt;
3444
3445     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
3446
3447     if(!graphics || !result)
3448         return InvalidParameter;
3449
3450     if(graphics->busy)
3451         return ObjectBusy;
3452
3453     pt.X = x;
3454     pt.Y = y;
3455     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3456                    CoordinateSpaceWorld, &pt, 1)) != Ok)
3457         return stat;
3458
3459     if((stat = GdipCreateRegion(&rgn)) != Ok)
3460         return stat;
3461
3462     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3463         goto cleanup;
3464
3465     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
3466
3467 cleanup:
3468     GdipDeleteRegion(rgn);
3469     return stat;
3470 }
3471
3472 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3473 {
3474     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
3475 }
3476
3477 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
3478 {
3479     GpStatus stat;
3480     GpRegion* rgn;
3481     GpPointF pts[2];
3482
3483     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
3484
3485     if(!graphics || !result)
3486         return InvalidParameter;
3487
3488     if(graphics->busy)
3489         return ObjectBusy;
3490
3491     pts[0].X = x;
3492     pts[0].Y = y;
3493     pts[1].X = x + width;
3494     pts[1].Y = y + height;
3495
3496     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3497                     CoordinateSpaceWorld, pts, 2)) != Ok)
3498         return stat;
3499
3500     pts[1].X -= pts[0].X;
3501     pts[1].Y -= pts[0].Y;
3502
3503     if((stat = GdipCreateRegion(&rgn)) != Ok)
3504         return stat;
3505
3506     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3507         goto cleanup;
3508
3509     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
3510
3511 cleanup:
3512     GdipDeleteRegion(rgn);
3513     return stat;
3514 }
3515
3516 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
3517 {
3518     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
3519 }
3520
3521 typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
3522     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3523     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3524     INT lineno, const RectF *bounds, void *user_data);
3525
3526 static GpStatus gdip_format_string(GpGraphics *graphics,
3527     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3528     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3529     gdip_format_string_callback callback, void *user_data)
3530 {
3531     WCHAR* stringdup;
3532     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
3533         nheight, lineend, lineno = 0;
3534     RectF bounds;
3535     StringAlignment halign;
3536     GpStatus stat = Ok;
3537     SIZE size;
3538
3539     if(length == -1) length = lstrlenW(string);
3540
3541     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
3542     if(!stringdup) return OutOfMemory;
3543
3544     nwidth = roundr(rect->Width);
3545     nheight = roundr(rect->Height);
3546
3547     if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
3548     if (rect->Height >= INT_MAX || rect->Width < 0.5) nheight = INT_MAX;
3549
3550     for(i = 0, j = 0; i < length; i++){
3551         /* FIXME: This makes the indexes passed to callback inaccurate. */
3552         if(!isprintW(string[i]) && (string[i] != '\n'))
3553             continue;
3554
3555         stringdup[j] = string[i];
3556         j++;
3557     }
3558
3559     length = j;
3560
3561     if (format) halign = format->align;
3562     else halign = StringAlignmentNear;
3563
3564     while(sum < length){
3565         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
3566                               nwidth, &fit, NULL, &size);
3567         fitcpy = fit;
3568
3569         if(fit == 0)
3570             break;
3571
3572         for(lret = 0; lret < fit; lret++)
3573             if(*(stringdup + sum + lret) == '\n')
3574                 break;
3575
3576         /* Line break code (may look strange, but it imitates windows). */
3577         if(lret < fit)
3578             lineend = fit = lret;    /* this is not an off-by-one error */
3579         else if(fit < (length - sum)){
3580             if(*(stringdup + sum + fit) == ' ')
3581                 while(*(stringdup + sum + fit) == ' ')
3582                     fit++;
3583             else
3584                 while(*(stringdup + sum + fit - 1) != ' '){
3585                     fit--;
3586
3587                     if(*(stringdup + sum + fit) == '\t')
3588                         break;
3589
3590                     if(fit == 0){
3591                         fit = fitcpy;
3592                         break;
3593                     }
3594                 }
3595             lineend = fit;
3596             while(*(stringdup + sum + lineend - 1) == ' ' ||
3597                   *(stringdup + sum + lineend - 1) == '\t')
3598                 lineend--;
3599         }
3600         else
3601             lineend = fit;
3602
3603         GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
3604                               nwidth, &j, NULL, &size);
3605
3606         bounds.Width = size.cx;
3607
3608         if(height + size.cy > nheight)
3609             bounds.Height = nheight - (height + size.cy);
3610         else
3611             bounds.Height = size.cy;
3612
3613         bounds.Y = rect->Y + height;
3614
3615         switch (halign)
3616         {
3617         case StringAlignmentNear:
3618         default:
3619             bounds.X = rect->X;
3620             break;
3621         case StringAlignmentCenter:
3622             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
3623             break;
3624         case StringAlignmentFar:
3625             bounds.X = rect->X + rect->Width - bounds.Width;
3626             break;
3627         }
3628
3629         stat = callback(graphics, stringdup, sum, lineend,
3630             font, rect, format, lineno, &bounds, user_data);
3631
3632         if (stat != Ok)
3633             break;
3634
3635         sum += fit + (lret < fitcpy ? 1 : 0);
3636         height += size.cy;
3637         lineno++;
3638
3639         if(height > nheight)
3640             break;
3641
3642         /* Stop if this was a linewrap (but not if it was a linebreak). */
3643         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3644             break;
3645     }
3646
3647     GdipFree(stringdup);
3648
3649     return stat;
3650 }
3651
3652 struct measure_ranges_args {
3653     GpRegion **regions;
3654 };
3655
3656 static GpStatus measure_ranges_callback(GpGraphics *graphics,
3657     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3658     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3659     INT lineno, const RectF *bounds, void *user_data)
3660 {
3661     int i;
3662     GpStatus stat = Ok;
3663     struct measure_ranges_args *args = user_data;
3664
3665     for (i=0; i<format->range_count; i++)
3666     {
3667         INT range_start = max(index, format->character_ranges[i].First);
3668         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
3669         if (range_start < range_end)
3670         {
3671             GpRectF range_rect;
3672             SIZE range_size;
3673
3674             range_rect.Y = bounds->Y;
3675             range_rect.Height = bounds->Height;
3676
3677             GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
3678                                   INT_MAX, NULL, NULL, &range_size);
3679             range_rect.X = bounds->X + range_size.cx;
3680
3681             GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
3682                                   INT_MAX, NULL, NULL, &range_size);
3683             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
3684
3685             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
3686             if (stat != Ok)
3687                 break;
3688         }
3689     }
3690
3691     return stat;
3692 }
3693
3694 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
3695         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
3696         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
3697         INT regionCount, GpRegion** regions)
3698 {
3699     GpStatus stat;
3700     int i;
3701     HFONT oldfont;
3702     struct measure_ranges_args args;
3703
3704     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
3705             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
3706
3707     if (!(graphics && string && font && layoutRect && stringFormat && regions))
3708         return InvalidParameter;
3709
3710     if (regionCount < stringFormat->range_count)
3711         return InvalidParameter;
3712
3713     if (stringFormat->attr)
3714         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
3715
3716     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3717
3718     for (i=0; i<stringFormat->range_count; i++)
3719     {
3720         stat = GdipSetEmpty(regions[i]);
3721         if (stat != Ok)
3722             return stat;
3723     }
3724
3725     args.regions = regions;
3726
3727     stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
3728         measure_ranges_callback, &args);
3729
3730     DeleteObject(SelectObject(graphics->hdc, oldfont));
3731
3732     return stat;
3733 }
3734
3735 struct measure_string_args {
3736     RectF *bounds;
3737     INT *codepointsfitted;
3738     INT *linesfilled;
3739 };
3740
3741 static GpStatus measure_string_callback(GpGraphics *graphics,
3742     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3743     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3744     INT lineno, const RectF *bounds, void *user_data)
3745 {
3746     struct measure_string_args *args = user_data;
3747
3748     if (bounds->Width > args->bounds->Width)
3749         args->bounds->Width = bounds->Width;
3750
3751     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
3752         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
3753
3754     if (args->codepointsfitted)
3755         *args->codepointsfitted = index + length;
3756
3757     if (args->linesfilled)
3758         (*args->linesfilled)++;
3759
3760     return Ok;
3761 }
3762
3763 /* Find the smallest rectangle that bounds the text when it is printed in rect
3764  * according to the format options listed in format. If rect has 0 width and
3765  * height, then just find the smallest rectangle that bounds the text when it's
3766  * printed at location (rect->X, rect-Y). */
3767 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
3768     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3769     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
3770     INT *codepointsfitted, INT *linesfilled)
3771 {
3772     HFONT oldfont;
3773     struct measure_string_args args;
3774
3775     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
3776         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
3777         bounds, codepointsfitted, linesfilled);
3778
3779     if(!graphics || !string || !font || !rect || !bounds)
3780         return InvalidParameter;
3781
3782     if(linesfilled) *linesfilled = 0;
3783     if(codepointsfitted) *codepointsfitted = 0;
3784
3785     if(format)
3786         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3787
3788     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3789
3790     bounds->X = rect->X;
3791     bounds->Y = rect->Y;
3792     bounds->Width = 0.0;
3793     bounds->Height = 0.0;
3794
3795     args.bounds = bounds;
3796     args.codepointsfitted = codepointsfitted;
3797     args.linesfilled = linesfilled;
3798
3799     gdip_format_string(graphics, string, length, font, rect, format,
3800         measure_string_callback, &args);
3801
3802     DeleteObject(SelectObject(graphics->hdc, oldfont));
3803
3804     return Ok;
3805 }
3806
3807 struct draw_string_args {
3808     POINT drawbase;
3809     UINT drawflags;
3810     REAL ang_cos, ang_sin;
3811 };
3812
3813 static GpStatus draw_string_callback(GpGraphics *graphics,
3814     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3815     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3816     INT lineno, const RectF *bounds, void *user_data)
3817 {
3818     struct draw_string_args *args = user_data;
3819     RECT drawcoord;
3820
3821     drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
3822     drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
3823
3824     DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
3825
3826     return Ok;
3827 }
3828
3829 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
3830     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
3831     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
3832 {
3833     HRGN rgn = NULL;
3834     HFONT gdifont;
3835     LOGFONTW lfw;
3836     TEXTMETRICW textmet;
3837     GpPointF pt[3], rectcpy[4];
3838     POINT corners[4];
3839     REAL angle, rel_width, rel_height;
3840     INT offsety = 0, save_state;
3841     struct draw_string_args args;
3842     RectF scaled_rect;
3843
3844     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
3845         length, font, debugstr_rectf(rect), format, brush);
3846
3847     if(!graphics || !string || !font || !brush || !rect)
3848         return InvalidParameter;
3849
3850     if((brush->bt != BrushTypeSolidColor)){
3851         FIXME("not implemented for given parameters\n");
3852         return NotImplemented;
3853     }
3854
3855     if(format){
3856         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3857
3858         /* Should be no need to explicitly test for StringAlignmentNear as
3859          * that is default behavior if no alignment is passed. */
3860         if(format->vertalign != StringAlignmentNear){
3861             RectF bounds;
3862             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
3863
3864             if(format->vertalign == StringAlignmentCenter)
3865                 offsety = (rect->Height - bounds.Height) / 2;
3866             else if(format->vertalign == StringAlignmentFar)
3867                 offsety = (rect->Height - bounds.Height);
3868         }
3869     }
3870
3871     save_state = SaveDC(graphics->hdc);
3872     SetBkMode(graphics->hdc, TRANSPARENT);
3873     SetTextColor(graphics->hdc, brush->lb.lbColor);
3874
3875     pt[0].X = 0.0;
3876     pt[0].Y = 0.0;
3877     pt[1].X = 1.0;
3878     pt[1].Y = 0.0;
3879     pt[2].X = 0.0;
3880     pt[2].Y = 1.0;
3881     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
3882     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
3883     args.ang_cos = cos(angle);
3884     args.ang_sin = sin(angle);
3885     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
3886                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
3887     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
3888                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
3889
3890     rectcpy[3].X = rectcpy[0].X = rect->X;
3891     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
3892     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
3893     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
3894     transform_and_round_points(graphics, corners, rectcpy, 4);
3895
3896     scaled_rect.X = 0.0;
3897     scaled_rect.Y = 0.0;
3898     scaled_rect.Width = rel_width * rect->Width;
3899     scaled_rect.Height = rel_height * rect->Height;
3900
3901     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
3902     {
3903         /* FIXME: If only the width or only the height is 0, we should probably still clip */
3904         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
3905         SelectClipRgn(graphics->hdc, rgn);
3906     }
3907
3908     /* Use gdi to find the font, then perform transformations on it (height,
3909      * width, angle). */
3910     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3911     GetTextMetricsW(graphics->hdc, &textmet);
3912     lfw = font->lfw;
3913
3914     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
3915     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
3916
3917     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
3918
3919     gdifont = CreateFontIndirectW(&lfw);
3920     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
3921
3922     if (!format || format->align == StringAlignmentNear)
3923     {
3924         args.drawbase.x = corners[0].x;
3925         args.drawbase.y = corners[0].y;
3926         args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
3927     }
3928     else if (format->align == StringAlignmentCenter)
3929     {
3930         args.drawbase.x = (corners[0].x + corners[1].x)/2;
3931         args.drawbase.y = (corners[0].y + corners[1].y)/2;
3932         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
3933     }
3934     else /* (format->align == StringAlignmentFar) */
3935     {
3936         args.drawbase.x = corners[1].x;
3937         args.drawbase.y = corners[1].y;
3938         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
3939     }
3940
3941     gdip_format_string(graphics, string, length, font, &scaled_rect, format,
3942         draw_string_callback, &args);
3943
3944     DeleteObject(rgn);
3945     DeleteObject(gdifont);
3946
3947     RestoreDC(graphics->hdc, save_state);
3948
3949     return Ok;
3950 }
3951
3952 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3953 {
3954     TRACE("(%p)\n", graphics);
3955
3956     if(!graphics)
3957         return InvalidParameter;
3958
3959     if(graphics->busy)
3960         return ObjectBusy;
3961
3962     return GdipSetInfinite(graphics->clip);
3963 }
3964
3965 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3966 {
3967     TRACE("(%p)\n", graphics);
3968
3969     if(!graphics)
3970         return InvalidParameter;
3971
3972     if(graphics->busy)
3973         return ObjectBusy;
3974
3975     graphics->worldtrans->matrix[0] = 1.0;
3976     graphics->worldtrans->matrix[1] = 0.0;
3977     graphics->worldtrans->matrix[2] = 0.0;
3978     graphics->worldtrans->matrix[3] = 1.0;
3979     graphics->worldtrans->matrix[4] = 0.0;
3980     graphics->worldtrans->matrix[5] = 0.0;
3981
3982     return Ok;
3983 }
3984
3985 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3986 {
3987     return GdipEndContainer(graphics, state);
3988 }
3989
3990 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3991     GpMatrixOrder order)
3992 {
3993     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3994
3995     if(!graphics)
3996         return InvalidParameter;
3997
3998     if(graphics->busy)
3999         return ObjectBusy;
4000
4001     return GdipRotateMatrix(graphics->worldtrans, angle, order);
4002 }
4003
4004 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
4005 {
4006     return GdipBeginContainer2(graphics, state);
4007 }
4008
4009 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
4010         GraphicsContainer *state)
4011 {
4012     GraphicsContainerItem *container;
4013     GpStatus sts;
4014
4015     TRACE("(%p, %p)\n", graphics, state);
4016
4017     if(!graphics || !state)
4018         return InvalidParameter;
4019
4020     sts = init_container(&container, graphics);
4021     if(sts != Ok)
4022         return sts;
4023
4024     list_add_head(&graphics->containers, &container->entry);
4025     *state = graphics->contid = container->contid;
4026
4027     return Ok;
4028 }
4029
4030 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
4031 {
4032     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4033     return NotImplemented;
4034 }
4035
4036 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *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 GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4043 {
4044     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4045     return NotImplemented;
4046 }
4047
4048 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4049 {
4050     GpStatus sts;
4051     GraphicsContainerItem *container, *container2;
4052
4053     TRACE("(%p, %x)\n", graphics, state);
4054
4055     if(!graphics)
4056         return InvalidParameter;
4057
4058     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4059         if(container->contid == state)
4060             break;
4061     }
4062
4063     /* did not find a matching container */
4064     if(&container->entry == &graphics->containers)
4065         return Ok;
4066
4067     sts = restore_container(graphics, container);
4068     if(sts != Ok)
4069         return sts;
4070
4071     /* remove all of the containers on top of the found container */
4072     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4073         if(container->contid == state)
4074             break;
4075         list_remove(&container->entry);
4076         delete_container(container);
4077     }
4078
4079     list_remove(&container->entry);
4080     delete_container(container);
4081
4082     return Ok;
4083 }
4084
4085 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4086     REAL sy, GpMatrixOrder order)
4087 {
4088     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4089
4090     if(!graphics)
4091         return InvalidParameter;
4092
4093     if(graphics->busy)
4094         return ObjectBusy;
4095
4096     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4097 }
4098
4099 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4100     CombineMode mode)
4101 {
4102     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4103
4104     if(!graphics || !srcgraphics)
4105         return InvalidParameter;
4106
4107     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4108 }
4109
4110 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4111     CompositingMode mode)
4112 {
4113     TRACE("(%p, %d)\n", graphics, mode);
4114
4115     if(!graphics)
4116         return InvalidParameter;
4117
4118     if(graphics->busy)
4119         return ObjectBusy;
4120
4121     graphics->compmode = mode;
4122
4123     return Ok;
4124 }
4125
4126 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4127     CompositingQuality quality)
4128 {
4129     TRACE("(%p, %d)\n", graphics, quality);
4130
4131     if(!graphics)
4132         return InvalidParameter;
4133
4134     if(graphics->busy)
4135         return ObjectBusy;
4136
4137     graphics->compqual = quality;
4138
4139     return Ok;
4140 }
4141
4142 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4143     InterpolationMode mode)
4144 {
4145     TRACE("(%p, %d)\n", graphics, mode);
4146
4147     if(!graphics)
4148         return InvalidParameter;
4149
4150     if(graphics->busy)
4151         return ObjectBusy;
4152
4153     graphics->interpolation = mode;
4154
4155     return Ok;
4156 }
4157
4158 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4159 {
4160     TRACE("(%p, %.2f)\n", graphics, scale);
4161
4162     if(!graphics || (scale <= 0.0))
4163         return InvalidParameter;
4164
4165     if(graphics->busy)
4166         return ObjectBusy;
4167
4168     graphics->scale = scale;
4169
4170     return Ok;
4171 }
4172
4173 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4174 {
4175     TRACE("(%p, %d)\n", graphics, unit);
4176
4177     if(!graphics)
4178         return InvalidParameter;
4179
4180     if(graphics->busy)
4181         return ObjectBusy;
4182
4183     if(unit == UnitWorld)
4184         return InvalidParameter;
4185
4186     graphics->unit = unit;
4187
4188     return Ok;
4189 }
4190
4191 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4192     mode)
4193 {
4194     TRACE("(%p, %d)\n", graphics, mode);
4195
4196     if(!graphics)
4197         return InvalidParameter;
4198
4199     if(graphics->busy)
4200         return ObjectBusy;
4201
4202     graphics->pixeloffset = mode;
4203
4204     return Ok;
4205 }
4206
4207 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
4208 {
4209     static int calls;
4210
4211     TRACE("(%p,%i,%i)\n", graphics, x, y);
4212
4213     if (!(calls++))
4214         FIXME("not implemented\n");
4215
4216     return NotImplemented;
4217 }
4218
4219 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
4220 {
4221     static int calls;
4222
4223     TRACE("(%p,%p,%p)\n", graphics, x, y);
4224
4225     if (!(calls++))
4226         FIXME("not implemented\n");
4227
4228     *x = *y = 0;
4229
4230     return NotImplemented;
4231 }
4232
4233 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
4234 {
4235     TRACE("(%p, %d)\n", graphics, mode);
4236
4237     if(!graphics)
4238         return InvalidParameter;
4239
4240     if(graphics->busy)
4241         return ObjectBusy;
4242
4243     graphics->smoothing = mode;
4244
4245     return Ok;
4246 }
4247
4248 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
4249 {
4250     TRACE("(%p, %d)\n", graphics, contrast);
4251
4252     if(!graphics)
4253         return InvalidParameter;
4254
4255     graphics->textcontrast = contrast;
4256
4257     return Ok;
4258 }
4259
4260 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
4261     TextRenderingHint hint)
4262 {
4263     TRACE("(%p, %d)\n", graphics, hint);
4264
4265     if(!graphics)
4266         return InvalidParameter;
4267
4268     if(graphics->busy)
4269         return ObjectBusy;
4270
4271     graphics->texthint = hint;
4272
4273     return Ok;
4274 }
4275
4276 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4277 {
4278     TRACE("(%p, %p)\n", graphics, matrix);
4279
4280     if(!graphics || !matrix)
4281         return InvalidParameter;
4282
4283     if(graphics->busy)
4284         return ObjectBusy;
4285
4286     GdipDeleteMatrix(graphics->worldtrans);
4287     return GdipCloneMatrix(matrix, &graphics->worldtrans);
4288 }
4289
4290 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
4291     REAL dy, GpMatrixOrder order)
4292 {
4293     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
4294
4295     if(!graphics)
4296         return InvalidParameter;
4297
4298     if(graphics->busy)
4299         return ObjectBusy;
4300
4301     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
4302 }
4303
4304 /*****************************************************************************
4305  * GdipSetClipHrgn [GDIPLUS.@]
4306  */
4307 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
4308 {
4309     GpRegion *region;
4310     GpStatus status;
4311
4312     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
4313
4314     if(!graphics)
4315         return InvalidParameter;
4316
4317     status = GdipCreateRegionHrgn(hrgn, &region);
4318     if(status != Ok)
4319         return status;
4320
4321     status = GdipSetClipRegion(graphics, region, mode);
4322
4323     GdipDeleteRegion(region);
4324     return status;
4325 }
4326
4327 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
4328 {
4329     TRACE("(%p, %p, %d)\n", graphics, path, mode);
4330
4331     if(!graphics)
4332         return InvalidParameter;
4333
4334     if(graphics->busy)
4335         return ObjectBusy;
4336
4337     return GdipCombineRegionPath(graphics->clip, path, mode);
4338 }
4339
4340 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
4341                                     REAL width, REAL height,
4342                                     CombineMode mode)
4343 {
4344     GpRectF rect;
4345
4346     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
4347
4348     if(!graphics)
4349         return InvalidParameter;
4350
4351     if(graphics->busy)
4352         return ObjectBusy;
4353
4354     rect.X = x;
4355     rect.Y = y;
4356     rect.Width  = width;
4357     rect.Height = height;
4358
4359     return GdipCombineRegionRect(graphics->clip, &rect, mode);
4360 }
4361
4362 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
4363                                      INT width, INT height,
4364                                      CombineMode mode)
4365 {
4366     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
4367
4368     if(!graphics)
4369         return InvalidParameter;
4370
4371     if(graphics->busy)
4372         return ObjectBusy;
4373
4374     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
4375 }
4376
4377 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
4378                                       CombineMode mode)
4379 {
4380     TRACE("(%p, %p, %d)\n", graphics, region, mode);
4381
4382     if(!graphics || !region)
4383         return InvalidParameter;
4384
4385     if(graphics->busy)
4386         return ObjectBusy;
4387
4388     return GdipCombineRegionRegion(graphics->clip, region, mode);
4389 }
4390
4391 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4392     UINT limitDpi)
4393 {
4394     static int calls;
4395
4396     TRACE("(%p,%u)\n", metafile, limitDpi);
4397
4398     if(!(calls++))
4399         FIXME("not implemented\n");
4400
4401     return NotImplemented;
4402 }
4403
4404 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
4405     INT count)
4406 {
4407     INT save_state;
4408     POINT *pti;
4409
4410     TRACE("(%p, %p, %d)\n", graphics, points, count);
4411
4412     if(!graphics || !pen || count<=0)
4413         return InvalidParameter;
4414
4415     if(graphics->busy)
4416         return ObjectBusy;
4417
4418     pti = GdipAlloc(sizeof(POINT) * count);
4419
4420     save_state = prepare_dc(graphics, pen);
4421     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
4422
4423     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
4424     Polygon(graphics->hdc, pti, count);
4425
4426     restore_dc(graphics, save_state);
4427     GdipFree(pti);
4428
4429     return Ok;
4430 }
4431
4432 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
4433     INT count)
4434 {
4435     GpStatus ret;
4436     GpPointF *ptf;
4437     INT i;
4438
4439     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
4440
4441     if(count<=0)    return InvalidParameter;
4442     ptf = GdipAlloc(sizeof(GpPointF) * count);
4443
4444     for(i = 0;i < count; i++){
4445         ptf[i].X = (REAL)points[i].X;
4446         ptf[i].Y = (REAL)points[i].Y;
4447     }
4448
4449     ret = GdipDrawPolygon(graphics,pen,ptf,count);
4450     GdipFree(ptf);
4451
4452     return ret;
4453 }
4454
4455 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
4456 {
4457     TRACE("(%p, %p)\n", graphics, dpi);
4458
4459     if(!graphics || !dpi)
4460         return InvalidParameter;
4461
4462     if(graphics->busy)
4463         return ObjectBusy;
4464
4465     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
4466
4467     return Ok;
4468 }
4469
4470 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
4471 {
4472     TRACE("(%p, %p)\n", graphics, dpi);
4473
4474     if(!graphics || !dpi)
4475         return InvalidParameter;
4476
4477     if(graphics->busy)
4478         return ObjectBusy;
4479
4480     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
4481
4482     return Ok;
4483 }
4484
4485 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
4486     GpMatrixOrder order)
4487 {
4488     GpMatrix m;
4489     GpStatus ret;
4490
4491     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
4492
4493     if(!graphics || !matrix)
4494         return InvalidParameter;
4495
4496     if(graphics->busy)
4497         return ObjectBusy;
4498
4499     m = *(graphics->worldtrans);
4500
4501     ret = GdipMultiplyMatrix(&m, matrix, order);
4502     if(ret == Ok)
4503         *(graphics->worldtrans) = m;
4504
4505     return ret;
4506 }
4507
4508 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
4509 {
4510     TRACE("(%p, %p)\n", graphics, hdc);
4511
4512     if(!graphics || !hdc)
4513         return InvalidParameter;
4514
4515     if(graphics->busy)
4516         return ObjectBusy;
4517
4518     *hdc = graphics->hdc;
4519     graphics->busy = TRUE;
4520
4521     return Ok;
4522 }
4523
4524 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
4525 {
4526     TRACE("(%p, %p)\n", graphics, hdc);
4527
4528     if(!graphics)
4529         return InvalidParameter;
4530
4531     if(graphics->hdc != hdc || !(graphics->busy))
4532         return InvalidParameter;
4533
4534     graphics->busy = FALSE;
4535
4536     return Ok;
4537 }
4538
4539 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
4540 {
4541     GpRegion *clip;
4542     GpStatus status;
4543
4544     TRACE("(%p, %p)\n", graphics, region);
4545
4546     if(!graphics || !region)
4547         return InvalidParameter;
4548
4549     if(graphics->busy)
4550         return ObjectBusy;
4551
4552     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
4553         return status;
4554
4555     /* free everything except root node and header */
4556     delete_element(&region->node);
4557     memcpy(region, clip, sizeof(GpRegion));
4558     GdipFree(clip);
4559
4560     return Ok;
4561 }
4562
4563 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
4564                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
4565 {
4566     GpMatrix *matrix;
4567     GpStatus stat;
4568     REAL unitscale;
4569
4570     if(!graphics || !points || count <= 0)
4571         return InvalidParameter;
4572
4573     if(graphics->busy)
4574         return ObjectBusy;
4575
4576     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4577
4578     if (src_space == dst_space) return Ok;
4579
4580     stat = GdipCreateMatrix(&matrix);
4581     if (stat == Ok)
4582     {
4583         unitscale = convert_unit(graphics->hdc, graphics->unit);
4584
4585         if(graphics->unit != UnitDisplay)
4586             unitscale *= graphics->scale;
4587
4588         /* transform from src_space to CoordinateSpacePage */
4589         switch (src_space)
4590         {
4591         case CoordinateSpaceWorld:
4592             GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
4593             break;
4594         case CoordinateSpacePage:
4595             break;
4596         case CoordinateSpaceDevice:
4597             GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
4598             break;
4599         }
4600
4601         /* transform from CoordinateSpacePage to dst_space */
4602         switch (dst_space)
4603         {
4604         case CoordinateSpaceWorld:
4605             {
4606                 GpMatrix *inverted_transform;
4607                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
4608                 if (stat == Ok)
4609                 {
4610                     stat = GdipInvertMatrix(inverted_transform);
4611                     if (stat == Ok)
4612                         GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
4613                     GdipDeleteMatrix(inverted_transform);
4614                 }
4615                 break;
4616             }
4617         case CoordinateSpacePage:
4618             break;
4619         case CoordinateSpaceDevice:
4620             GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
4621             break;
4622         }
4623
4624         if (stat == Ok)
4625             stat = GdipTransformMatrixPoints(matrix, points, count);
4626
4627         GdipDeleteMatrix(matrix);
4628     }
4629
4630     return stat;
4631 }
4632
4633 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
4634                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
4635 {
4636     GpPointF *pointsF;
4637     GpStatus ret;
4638     INT i;
4639
4640     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4641
4642     if(count <= 0)
4643         return InvalidParameter;
4644
4645     pointsF = GdipAlloc(sizeof(GpPointF) * count);
4646     if(!pointsF)
4647         return OutOfMemory;
4648
4649     for(i = 0; i < count; i++){
4650         pointsF[i].X = (REAL)points[i].X;
4651         pointsF[i].Y = (REAL)points[i].Y;
4652     }
4653
4654     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
4655
4656     if(ret == Ok)
4657         for(i = 0; i < count; i++){
4658             points[i].X = roundr(pointsF[i].X);
4659             points[i].Y = roundr(pointsF[i].Y);
4660         }
4661     GdipFree(pointsF);
4662
4663     return ret;
4664 }
4665
4666 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
4667 {
4668     static int calls;
4669
4670     TRACE("\n");
4671
4672     if (!calls++)
4673       FIXME("stub\n");
4674
4675     return NULL;
4676 }
4677
4678 /*****************************************************************************
4679  * GdipTranslateClip [GDIPLUS.@]
4680  */
4681 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
4682 {
4683     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
4684
4685     if(!graphics)
4686         return InvalidParameter;
4687
4688     if(graphics->busy)
4689         return ObjectBusy;
4690
4691     return GdipTranslateRegion(graphics->clip, dx, dy);
4692 }
4693
4694 /*****************************************************************************
4695  * GdipTranslateClipI [GDIPLUS.@]
4696  */
4697 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
4698 {
4699     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
4700
4701     if(!graphics)
4702         return InvalidParameter;
4703
4704     if(graphics->busy)
4705         return ObjectBusy;
4706
4707     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
4708 }
4709
4710
4711 /*****************************************************************************
4712  * GdipMeasureDriverString [GDIPLUS.@]
4713  */
4714 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4715                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
4716                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
4717 {
4718     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
4719     return NotImplemented;
4720 }
4721
4722 /*****************************************************************************
4723  * GdipDrawDriverString [GDIPLUS.@]
4724  */
4725 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4726                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
4727                                          GDIPCONST PointF *positions, INT flags,
4728                                          GDIPCONST GpMatrix *matrix )
4729 {
4730     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
4731     return NotImplemented;
4732 }
4733
4734 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
4735                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4736 {
4737     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4738     return NotImplemented;
4739 }
4740
4741 /*****************************************************************************
4742  * GdipRecordMetafileI [GDIPLUS.@]
4743  */
4744 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4745                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4746 {
4747     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4748     return NotImplemented;
4749 }
4750
4751 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4752                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4753 {
4754     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
4755     return NotImplemented;
4756 }
4757
4758 /*****************************************************************************
4759  * GdipIsVisibleClipEmpty [GDIPLUS.@]
4760  */
4761 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
4762 {
4763     GpStatus stat;
4764     GpRegion* rgn;
4765
4766     TRACE("(%p, %p)\n", graphics, res);
4767
4768     if((stat = GdipCreateRegion(&rgn)) != Ok)
4769         return stat;
4770
4771     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4772         goto cleanup;
4773
4774     stat = GdipIsEmptyRegion(rgn, graphics, res);
4775
4776 cleanup:
4777     GdipDeleteRegion(rgn);
4778     return stat;
4779 }