gdiplus/tests: Remove variable path which is not really used from test_linei.
[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             for (x=dst_area.left; x<dst_area.right; x++)
2053             {
2054                 for (y=dst_area.top; y<dst_area.bottom; y++)
2055                 {
2056                     GpPointF src_pointf;
2057                     int src_x, src_y;
2058                     ARGB *src_color;
2059
2060                     src_pointf.X = x;
2061                     src_pointf.Y = y;
2062
2063                     GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
2064
2065                     src_x = roundr(src_pointf.X);
2066                     src_y = roundr(src_pointf.Y);
2067
2068                     src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2069
2070                     if (src_x < src_area.left || src_x >= src_area.right ||
2071                         src_y < src_area.top || src_y >= src_area.bottom)
2072                         /* FIXME: Use wrapmode */
2073                         *src_color = 0;
2074                     else
2075                         GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
2076                 }
2077             }
2078
2079             GdipDeleteMatrix(dst_to_src);
2080
2081             if (imageAttributes)
2082             {
2083                 if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
2084                     imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
2085                 {
2086                     static int fixme;
2087                     if (!fixme++)
2088                         FIXME("Color keying not implemented\n");
2089                 }
2090
2091                 if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
2092                     imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
2093                 {
2094                     const struct color_remap_table *table;
2095
2096                     if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
2097                         table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
2098                     else
2099                         table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
2100
2101                     for (x=dst_area.left; x<dst_area.right; x++)
2102                         for (y=dst_area.top; y<dst_area.bottom; y++)
2103                         {
2104                             ARGB *src_color;
2105                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2106                             for (i=0; i<table->mapsize; i++)
2107                             {
2108                                 if (*src_color == table->colormap[i].oldColor.Argb)
2109                                 {
2110                                     *src_color = table->colormap[i].newColor.Argb;
2111                                     break;
2112                                 }
2113                             }
2114                         }
2115                 }
2116
2117                 if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
2118                     imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
2119                 {
2120                     static int fixme;
2121                     if (!fixme++)
2122                         FIXME("Color transforms not implemented\n");
2123                 }
2124
2125                 if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
2126                     imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
2127                 {
2128                     static int fixme;
2129                     if (!fixme++)
2130                         FIXME("Gamma adjustment not implemented\n");
2131                 }
2132             }
2133
2134             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
2135                 data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
2136
2137             GdipFree(data);
2138
2139             return stat;
2140         }
2141         else
2142         {
2143             HDC hdc;
2144             int temp_hdc=0, temp_bitmap=0;
2145             HBITMAP hbitmap, old_hbm=NULL;
2146
2147             if (!(bitmap->format == PixelFormat16bppRGB555 ||
2148                   bitmap->format == PixelFormat24bppRGB ||
2149                   bitmap->format == PixelFormat32bppRGB ||
2150                   bitmap->format == PixelFormat32bppPARGB))
2151             {
2152                 BITMAPINFOHEADER bih;
2153                 BYTE *temp_bits;
2154                 PixelFormat dst_format;
2155
2156                 /* we can't draw a bitmap of this format directly */
2157                 hdc = CreateCompatibleDC(0);
2158                 temp_hdc = 1;
2159                 temp_bitmap = 1;
2160
2161                 bih.biSize = sizeof(BITMAPINFOHEADER);
2162                 bih.biWidth = bitmap->width;
2163                 bih.biHeight = -bitmap->height;
2164                 bih.biPlanes = 1;
2165                 bih.biBitCount = 32;
2166                 bih.biCompression = BI_RGB;
2167                 bih.biSizeImage = 0;
2168                 bih.biXPelsPerMeter = 0;
2169                 bih.biYPelsPerMeter = 0;
2170                 bih.biClrUsed = 0;
2171                 bih.biClrImportant = 0;
2172
2173                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
2174                     (void**)&temp_bits, NULL, 0);
2175
2176                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2177                     dst_format = PixelFormat32bppPARGB;
2178                 else
2179                     dst_format = PixelFormat32bppRGB;
2180
2181                 convert_pixels(bitmap->width, bitmap->height,
2182                     bitmap->width*4, temp_bits, dst_format,
2183                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
2184             }
2185             else
2186             {
2187                 hbitmap = bitmap->hbitmap;
2188                 hdc = bitmap->hdc;
2189                 temp_hdc = (hdc == 0);
2190             }
2191
2192             if (temp_hdc)
2193             {
2194                 if (!hdc) hdc = CreateCompatibleDC(0);
2195                 old_hbm = SelectObject(hdc, hbitmap);
2196             }
2197
2198             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2199             {
2200                 BLENDFUNCTION bf;
2201
2202                 bf.BlendOp = AC_SRC_OVER;
2203                 bf.BlendFlags = 0;
2204                 bf.SourceConstantAlpha = 255;
2205                 bf.AlphaFormat = AC_SRC_ALPHA;
2206
2207                 GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2208                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
2209             }
2210             else
2211             {
2212                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2213                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
2214             }
2215
2216             if (temp_hdc)
2217             {
2218                 SelectObject(hdc, old_hbm);
2219                 DeleteDC(hdc);
2220             }
2221
2222             if (temp_bitmap)
2223                 DeleteObject(hbitmap);
2224         }
2225     }
2226     else
2227     {
2228         ERR("GpImage with no IPicture or HBITMAP?!\n");
2229         return NotImplemented;
2230     }
2231
2232     return Ok;
2233 }
2234
2235 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
2236      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
2237      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2238      DrawImageAbort callback, VOID * callbackData)
2239 {
2240     GpPointF pointsF[3];
2241     INT i;
2242
2243     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
2244           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2245           callbackData);
2246
2247     if(!points || count!=3)
2248         return InvalidParameter;
2249
2250     for(i = 0; i < count; i++){
2251         pointsF[i].X = (REAL)points[i].X;
2252         pointsF[i].Y = (REAL)points[i].Y;
2253     }
2254
2255     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
2256                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
2257                                    callback, callbackData);
2258 }
2259
2260 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
2261     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
2262     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
2263     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
2264     VOID * callbackData)
2265 {
2266     GpPointF points[3];
2267
2268     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
2269           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2270           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2271
2272     points[0].X = dstx;
2273     points[0].Y = dsty;
2274     points[1].X = dstx + dstwidth;
2275     points[1].Y = dsty;
2276     points[2].X = dstx;
2277     points[2].Y = dsty + dstheight;
2278
2279     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2280                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2281 }
2282
2283 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
2284         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
2285         INT srcwidth, INT srcheight, GpUnit srcUnit,
2286         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
2287         VOID * callbackData)
2288 {
2289     GpPointF points[3];
2290
2291     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
2292           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2293           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2294
2295     points[0].X = dstx;
2296     points[0].Y = dsty;
2297     points[1].X = dstx + dstwidth;
2298     points[1].Y = dsty;
2299     points[2].X = dstx;
2300     points[2].Y = dsty + dstheight;
2301
2302     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2303                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2304 }
2305
2306 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
2307     REAL x, REAL y, REAL width, REAL height)
2308 {
2309     RectF bounds;
2310     GpUnit unit;
2311     GpStatus ret;
2312
2313     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
2314
2315     if(!graphics || !image)
2316         return InvalidParameter;
2317
2318     ret = GdipGetImageBounds(image, &bounds, &unit);
2319     if(ret != Ok)
2320         return ret;
2321
2322     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
2323                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
2324                                  unit, NULL, NULL, NULL);
2325 }
2326
2327 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
2328     INT x, INT y, INT width, INT height)
2329 {
2330     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
2331
2332     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
2333 }
2334
2335 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
2336     REAL y1, REAL x2, REAL y2)
2337 {
2338     INT save_state;
2339     GpPointF pt[2];
2340     GpStatus retval;
2341
2342     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
2343
2344     if(!pen || !graphics)
2345         return InvalidParameter;
2346
2347     if(graphics->busy)
2348         return ObjectBusy;
2349
2350     pt[0].X = x1;
2351     pt[0].Y = y1;
2352     pt[1].X = x2;
2353     pt[1].Y = y2;
2354
2355     save_state = prepare_dc(graphics, pen);
2356
2357     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2358
2359     restore_dc(graphics, save_state);
2360
2361     return retval;
2362 }
2363
2364 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
2365     INT y1, INT x2, INT y2)
2366 {
2367     INT save_state;
2368     GpPointF pt[2];
2369     GpStatus retval;
2370
2371     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
2372
2373     if(!pen || !graphics)
2374         return InvalidParameter;
2375
2376     if(graphics->busy)
2377         return ObjectBusy;
2378
2379     pt[0].X = (REAL)x1;
2380     pt[0].Y = (REAL)y1;
2381     pt[1].X = (REAL)x2;
2382     pt[1].Y = (REAL)y2;
2383
2384     save_state = prepare_dc(graphics, pen);
2385
2386     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2387
2388     restore_dc(graphics, save_state);
2389
2390     return retval;
2391 }
2392
2393 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
2394     GpPointF *points, INT count)
2395 {
2396     INT save_state;
2397     GpStatus retval;
2398
2399     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2400
2401     if(!pen || !graphics || (count < 2))
2402         return InvalidParameter;
2403
2404     if(graphics->busy)
2405         return ObjectBusy;
2406
2407     save_state = prepare_dc(graphics, pen);
2408
2409     retval = draw_polyline(graphics, pen, points, count, TRUE);
2410
2411     restore_dc(graphics, save_state);
2412
2413     return retval;
2414 }
2415
2416 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
2417     GpPoint *points, INT count)
2418 {
2419     INT save_state;
2420     GpStatus retval;
2421     GpPointF *ptf = NULL;
2422     int i;
2423
2424     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2425
2426     if(!pen || !graphics || (count < 2))
2427         return InvalidParameter;
2428
2429     if(graphics->busy)
2430         return ObjectBusy;
2431
2432     ptf = GdipAlloc(count * sizeof(GpPointF));
2433     if(!ptf) return OutOfMemory;
2434
2435     for(i = 0; i < count; i ++){
2436         ptf[i].X = (REAL) points[i].X;
2437         ptf[i].Y = (REAL) points[i].Y;
2438     }
2439
2440     save_state = prepare_dc(graphics, pen);
2441
2442     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
2443
2444     restore_dc(graphics, save_state);
2445
2446     GdipFree(ptf);
2447     return retval;
2448 }
2449
2450 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
2451 {
2452     INT save_state;
2453     GpStatus retval;
2454
2455     TRACE("(%p, %p, %p)\n", graphics, pen, path);
2456
2457     if(!pen || !graphics)
2458         return InvalidParameter;
2459
2460     if(graphics->busy)
2461         return ObjectBusy;
2462
2463     save_state = prepare_dc(graphics, pen);
2464
2465     retval = draw_poly(graphics, pen, path->pathdata.Points,
2466                        path->pathdata.Types, path->pathdata.Count, TRUE);
2467
2468     restore_dc(graphics, save_state);
2469
2470     return retval;
2471 }
2472
2473 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
2474     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2475 {
2476     INT save_state;
2477
2478     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2479             width, height, startAngle, sweepAngle);
2480
2481     if(!graphics || !pen)
2482         return InvalidParameter;
2483
2484     if(graphics->busy)
2485         return ObjectBusy;
2486
2487     save_state = prepare_dc(graphics, pen);
2488     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2489
2490     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2491
2492     restore_dc(graphics, save_state);
2493
2494     return Ok;
2495 }
2496
2497 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
2498     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2499 {
2500     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2501             width, height, startAngle, sweepAngle);
2502
2503     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2504 }
2505
2506 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
2507     REAL y, REAL width, REAL height)
2508 {
2509     INT save_state;
2510     GpPointF ptf[4];
2511     POINT pti[4];
2512
2513     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2514
2515     if(!pen || !graphics)
2516         return InvalidParameter;
2517
2518     if(graphics->busy)
2519         return ObjectBusy;
2520
2521     ptf[0].X = x;
2522     ptf[0].Y = y;
2523     ptf[1].X = x + width;
2524     ptf[1].Y = y;
2525     ptf[2].X = x + width;
2526     ptf[2].Y = y + height;
2527     ptf[3].X = x;
2528     ptf[3].Y = y + height;
2529
2530     save_state = prepare_dc(graphics, pen);
2531     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2532
2533     transform_and_round_points(graphics, pti, ptf, 4);
2534     Polygon(graphics->hdc, pti, 4);
2535
2536     restore_dc(graphics, save_state);
2537
2538     return Ok;
2539 }
2540
2541 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
2542     INT y, INT width, INT height)
2543 {
2544     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2545
2546     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2547 }
2548
2549 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
2550     GDIPCONST GpRectF* rects, INT count)
2551 {
2552     GpPointF *ptf;
2553     POINT *pti;
2554     INT save_state, i;
2555
2556     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2557
2558     if(!graphics || !pen || !rects || count < 1)
2559         return InvalidParameter;
2560
2561     if(graphics->busy)
2562         return ObjectBusy;
2563
2564     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
2565     pti = GdipAlloc(4 * count * sizeof(POINT));
2566
2567     if(!ptf || !pti){
2568         GdipFree(ptf);
2569         GdipFree(pti);
2570         return OutOfMemory;
2571     }
2572
2573     for(i = 0; i < count; i++){
2574         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
2575         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
2576         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
2577         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
2578     }
2579
2580     save_state = prepare_dc(graphics, pen);
2581     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2582
2583     transform_and_round_points(graphics, pti, ptf, 4 * count);
2584
2585     for(i = 0; i < count; i++)
2586         Polygon(graphics->hdc, &pti[4 * i], 4);
2587
2588     restore_dc(graphics, save_state);
2589
2590     GdipFree(ptf);
2591     GdipFree(pti);
2592
2593     return Ok;
2594 }
2595
2596 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
2597     GDIPCONST GpRect* rects, INT count)
2598 {
2599     GpRectF *rectsF;
2600     GpStatus ret;
2601     INT i;
2602
2603     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2604
2605     if(!rects || count<=0)
2606         return InvalidParameter;
2607
2608     rectsF = GdipAlloc(sizeof(GpRectF) * count);
2609     if(!rectsF)
2610         return OutOfMemory;
2611
2612     for(i = 0;i < count;i++){
2613         rectsF[i].X      = (REAL)rects[i].X;
2614         rectsF[i].Y      = (REAL)rects[i].Y;
2615         rectsF[i].Width  = (REAL)rects[i].Width;
2616         rectsF[i].Height = (REAL)rects[i].Height;
2617     }
2618
2619     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
2620     GdipFree(rectsF);
2621
2622     return ret;
2623 }
2624
2625 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2626     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2627 {
2628     GpPath *path;
2629     GpStatus stat;
2630
2631     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2632             count, tension, fill);
2633
2634     if(!graphics || !brush || !points)
2635         return InvalidParameter;
2636
2637     if(graphics->busy)
2638         return ObjectBusy;
2639
2640     stat = GdipCreatePath(fill, &path);
2641     if(stat != Ok)
2642         return stat;
2643
2644     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2645     if(stat != Ok){
2646         GdipDeletePath(path);
2647         return stat;
2648     }
2649
2650     stat = GdipFillPath(graphics, brush, path);
2651     if(stat != Ok){
2652         GdipDeletePath(path);
2653         return stat;
2654     }
2655
2656     GdipDeletePath(path);
2657
2658     return Ok;
2659 }
2660
2661 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2662     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2663 {
2664     GpPointF *ptf;
2665     GpStatus stat;
2666     INT i;
2667
2668     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2669             count, tension, fill);
2670
2671     if(!points || count <= 0)
2672         return InvalidParameter;
2673
2674     ptf = GdipAlloc(sizeof(GpPointF)*count);
2675     if(!ptf)
2676         return OutOfMemory;
2677
2678     for(i = 0;i < count;i++){
2679         ptf[i].X = (REAL)points[i].X;
2680         ptf[i].Y = (REAL)points[i].Y;
2681     }
2682
2683     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2684
2685     GdipFree(ptf);
2686
2687     return stat;
2688 }
2689
2690 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2691     REAL y, REAL width, REAL height)
2692 {
2693     INT save_state;
2694     GpPointF ptf[2];
2695     POINT pti[2];
2696
2697     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2698
2699     if(!graphics || !brush)
2700         return InvalidParameter;
2701
2702     if(graphics->busy)
2703         return ObjectBusy;
2704
2705     ptf[0].X = x;
2706     ptf[0].Y = y;
2707     ptf[1].X = x + width;
2708     ptf[1].Y = y + height;
2709
2710     save_state = SaveDC(graphics->hdc);
2711     EndPath(graphics->hdc);
2712
2713     transform_and_round_points(graphics, pti, ptf, 2);
2714
2715     BeginPath(graphics->hdc);
2716     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2717     EndPath(graphics->hdc);
2718
2719     brush_fill_path(graphics, brush);
2720
2721     RestoreDC(graphics->hdc, save_state);
2722
2723     return Ok;
2724 }
2725
2726 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2727     INT y, INT width, INT height)
2728 {
2729     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2730
2731     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2732 }
2733
2734 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2735 {
2736     INT save_state;
2737     GpStatus retval;
2738
2739     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2740
2741     if(!brush || !graphics || !path)
2742         return InvalidParameter;
2743
2744     if(graphics->busy)
2745         return ObjectBusy;
2746
2747     save_state = SaveDC(graphics->hdc);
2748     EndPath(graphics->hdc);
2749     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2750                                                                     : WINDING));
2751
2752     BeginPath(graphics->hdc);
2753     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2754                        path->pathdata.Types, path->pathdata.Count, FALSE);
2755
2756     if(retval != Ok)
2757         goto end;
2758
2759     EndPath(graphics->hdc);
2760     brush_fill_path(graphics, brush);
2761
2762     retval = Ok;
2763
2764 end:
2765     RestoreDC(graphics->hdc, save_state);
2766
2767     return retval;
2768 }
2769
2770 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2771     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2772 {
2773     INT save_state;
2774
2775     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2776             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2777
2778     if(!graphics || !brush)
2779         return InvalidParameter;
2780
2781     if(graphics->busy)
2782         return ObjectBusy;
2783
2784     save_state = SaveDC(graphics->hdc);
2785     EndPath(graphics->hdc);
2786
2787     BeginPath(graphics->hdc);
2788     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2789     EndPath(graphics->hdc);
2790
2791     brush_fill_path(graphics, brush);
2792
2793     RestoreDC(graphics->hdc, save_state);
2794
2795     return Ok;
2796 }
2797
2798 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2799     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2800 {
2801     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2802             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2803
2804     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2805 }
2806
2807 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2808     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2809 {
2810     INT save_state;
2811     GpPointF *ptf = NULL;
2812     POINT *pti = NULL;
2813     GpStatus retval = Ok;
2814
2815     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2816
2817     if(!graphics || !brush || !points || !count)
2818         return InvalidParameter;
2819
2820     if(graphics->busy)
2821         return ObjectBusy;
2822
2823     ptf = GdipAlloc(count * sizeof(GpPointF));
2824     pti = GdipAlloc(count * sizeof(POINT));
2825     if(!ptf || !pti){
2826         retval = OutOfMemory;
2827         goto end;
2828     }
2829
2830     memcpy(ptf, points, count * sizeof(GpPointF));
2831
2832     save_state = SaveDC(graphics->hdc);
2833     EndPath(graphics->hdc);
2834     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2835                                                                   : WINDING));
2836
2837     transform_and_round_points(graphics, pti, ptf, count);
2838
2839     BeginPath(graphics->hdc);
2840     Polygon(graphics->hdc, pti, count);
2841     EndPath(graphics->hdc);
2842
2843     brush_fill_path(graphics, brush);
2844
2845     RestoreDC(graphics->hdc, save_state);
2846
2847 end:
2848     GdipFree(ptf);
2849     GdipFree(pti);
2850
2851     return retval;
2852 }
2853
2854 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2855     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2856 {
2857     INT save_state, i;
2858     GpPointF *ptf = NULL;
2859     POINT *pti = NULL;
2860     GpStatus retval = Ok;
2861
2862     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2863
2864     if(!graphics || !brush || !points || !count)
2865         return InvalidParameter;
2866
2867     if(graphics->busy)
2868         return ObjectBusy;
2869
2870     ptf = GdipAlloc(count * sizeof(GpPointF));
2871     pti = GdipAlloc(count * sizeof(POINT));
2872     if(!ptf || !pti){
2873         retval = OutOfMemory;
2874         goto end;
2875     }
2876
2877     for(i = 0; i < count; i ++){
2878         ptf[i].X = (REAL) points[i].X;
2879         ptf[i].Y = (REAL) points[i].Y;
2880     }
2881
2882     save_state = SaveDC(graphics->hdc);
2883     EndPath(graphics->hdc);
2884     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2885                                                                   : WINDING));
2886
2887     transform_and_round_points(graphics, pti, ptf, count);
2888
2889     BeginPath(graphics->hdc);
2890     Polygon(graphics->hdc, pti, count);
2891     EndPath(graphics->hdc);
2892
2893     brush_fill_path(graphics, brush);
2894
2895     RestoreDC(graphics->hdc, save_state);
2896
2897 end:
2898     GdipFree(ptf);
2899     GdipFree(pti);
2900
2901     return retval;
2902 }
2903
2904 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2905     GDIPCONST GpPointF *points, INT count)
2906 {
2907     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2908
2909     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2910 }
2911
2912 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2913     GDIPCONST GpPoint *points, INT count)
2914 {
2915     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2916
2917     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2918 }
2919
2920 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2921     REAL x, REAL y, REAL width, REAL height)
2922 {
2923     INT save_state;
2924     GpPointF ptf[4];
2925     POINT pti[4];
2926
2927     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2928
2929     if(!graphics || !brush)
2930         return InvalidParameter;
2931
2932     if(graphics->busy)
2933         return ObjectBusy;
2934
2935     ptf[0].X = x;
2936     ptf[0].Y = y;
2937     ptf[1].X = x + width;
2938     ptf[1].Y = y;
2939     ptf[2].X = x + width;
2940     ptf[2].Y = y + height;
2941     ptf[3].X = x;
2942     ptf[3].Y = y + height;
2943
2944     save_state = SaveDC(graphics->hdc);
2945     EndPath(graphics->hdc);
2946
2947     transform_and_round_points(graphics, pti, ptf, 4);
2948
2949     BeginPath(graphics->hdc);
2950     Polygon(graphics->hdc, pti, 4);
2951     EndPath(graphics->hdc);
2952
2953     brush_fill_path(graphics, brush);
2954
2955     RestoreDC(graphics->hdc, save_state);
2956
2957     return Ok;
2958 }
2959
2960 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2961     INT x, INT y, INT width, INT height)
2962 {
2963     INT save_state;
2964     GpPointF ptf[4];
2965     POINT pti[4];
2966
2967     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2968
2969     if(!graphics || !brush)
2970         return InvalidParameter;
2971
2972     if(graphics->busy)
2973         return ObjectBusy;
2974
2975     ptf[0].X = x;
2976     ptf[0].Y = y;
2977     ptf[1].X = x + width;
2978     ptf[1].Y = y;
2979     ptf[2].X = x + width;
2980     ptf[2].Y = y + height;
2981     ptf[3].X = x;
2982     ptf[3].Y = y + height;
2983
2984     save_state = SaveDC(graphics->hdc);
2985     EndPath(graphics->hdc);
2986
2987     transform_and_round_points(graphics, pti, ptf, 4);
2988
2989     BeginPath(graphics->hdc);
2990     Polygon(graphics->hdc, pti, 4);
2991     EndPath(graphics->hdc);
2992
2993     brush_fill_path(graphics, brush);
2994
2995     RestoreDC(graphics->hdc, save_state);
2996
2997     return Ok;
2998 }
2999
3000 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3001     INT count)
3002 {
3003     GpStatus ret;
3004     INT i;
3005
3006     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3007
3008     if(!rects)
3009         return InvalidParameter;
3010
3011     for(i = 0; i < count; i++){
3012         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3013         if(ret != Ok)   return ret;
3014     }
3015
3016     return Ok;
3017 }
3018
3019 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3020     INT count)
3021 {
3022     GpRectF *rectsF;
3023     GpStatus ret;
3024     INT i;
3025
3026     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3027
3028     if(!rects || count <= 0)
3029         return InvalidParameter;
3030
3031     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3032     if(!rectsF)
3033         return OutOfMemory;
3034
3035     for(i = 0; i < count; i++){
3036         rectsF[i].X      = (REAL)rects[i].X;
3037         rectsF[i].Y      = (REAL)rects[i].Y;
3038         rectsF[i].X      = (REAL)rects[i].Width;
3039         rectsF[i].Height = (REAL)rects[i].Height;
3040     }
3041
3042     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3043     GdipFree(rectsF);
3044
3045     return ret;
3046 }
3047
3048 /*****************************************************************************
3049  * GdipFillRegion [GDIPLUS.@]
3050  */
3051 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3052         GpRegion* region)
3053 {
3054     INT save_state;
3055     GpStatus status;
3056     HRGN hrgn;
3057     RECT rc;
3058
3059     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3060
3061     if (!(graphics && brush && region))
3062         return InvalidParameter;
3063
3064     if(graphics->busy)
3065         return ObjectBusy;
3066
3067     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3068     if(status != Ok)
3069         return status;
3070
3071     save_state = SaveDC(graphics->hdc);
3072     EndPath(graphics->hdc);
3073
3074     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3075
3076     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3077     {
3078         BeginPath(graphics->hdc);
3079         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3080         EndPath(graphics->hdc);
3081
3082         brush_fill_path(graphics, brush);
3083     }
3084
3085     RestoreDC(graphics->hdc, save_state);
3086
3087     DeleteObject(hrgn);
3088
3089     return Ok;
3090 }
3091
3092 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3093 {
3094     TRACE("(%p,%u)\n", graphics, intention);
3095
3096     if(!graphics)
3097         return InvalidParameter;
3098
3099     if(graphics->busy)
3100         return ObjectBusy;
3101
3102     /* We have no internal operation queue, so there's no need to clear it. */
3103
3104     if (graphics->hdc)
3105         GdiFlush();
3106
3107     return Ok;
3108 }
3109
3110 /*****************************************************************************
3111  * GdipGetClipBounds [GDIPLUS.@]
3112  */
3113 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3114 {
3115     TRACE("(%p, %p)\n", graphics, rect);
3116
3117     if(!graphics)
3118         return InvalidParameter;
3119
3120     if(graphics->busy)
3121         return ObjectBusy;
3122
3123     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3124 }
3125
3126 /*****************************************************************************
3127  * GdipGetClipBoundsI [GDIPLUS.@]
3128  */
3129 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3130 {
3131     TRACE("(%p, %p)\n", graphics, rect);
3132
3133     if(!graphics)
3134         return InvalidParameter;
3135
3136     if(graphics->busy)
3137         return ObjectBusy;
3138
3139     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3140 }
3141
3142 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3143 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3144     CompositingMode *mode)
3145 {
3146     TRACE("(%p, %p)\n", graphics, mode);
3147
3148     if(!graphics || !mode)
3149         return InvalidParameter;
3150
3151     if(graphics->busy)
3152         return ObjectBusy;
3153
3154     *mode = graphics->compmode;
3155
3156     return Ok;
3157 }
3158
3159 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3160 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3161     CompositingQuality *quality)
3162 {
3163     TRACE("(%p, %p)\n", graphics, quality);
3164
3165     if(!graphics || !quality)
3166         return InvalidParameter;
3167
3168     if(graphics->busy)
3169         return ObjectBusy;
3170
3171     *quality = graphics->compqual;
3172
3173     return Ok;
3174 }
3175
3176 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
3177 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
3178     InterpolationMode *mode)
3179 {
3180     TRACE("(%p, %p)\n", graphics, mode);
3181
3182     if(!graphics || !mode)
3183         return InvalidParameter;
3184
3185     if(graphics->busy)
3186         return ObjectBusy;
3187
3188     *mode = graphics->interpolation;
3189
3190     return Ok;
3191 }
3192
3193 /* FIXME: Need to handle color depths less than 24bpp */
3194 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
3195 {
3196     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
3197
3198     if(!graphics || !argb)
3199         return InvalidParameter;
3200
3201     if(graphics->busy)
3202         return ObjectBusy;
3203
3204     return Ok;
3205 }
3206
3207 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
3208 {
3209     TRACE("(%p, %p)\n", graphics, scale);
3210
3211     if(!graphics || !scale)
3212         return InvalidParameter;
3213
3214     if(graphics->busy)
3215         return ObjectBusy;
3216
3217     *scale = graphics->scale;
3218
3219     return Ok;
3220 }
3221
3222 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
3223 {
3224     TRACE("(%p, %p)\n", graphics, unit);
3225
3226     if(!graphics || !unit)
3227         return InvalidParameter;
3228
3229     if(graphics->busy)
3230         return ObjectBusy;
3231
3232     *unit = graphics->unit;
3233
3234     return Ok;
3235 }
3236
3237 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
3238 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3239     *mode)
3240 {
3241     TRACE("(%p, %p)\n", graphics, mode);
3242
3243     if(!graphics || !mode)
3244         return InvalidParameter;
3245
3246     if(graphics->busy)
3247         return ObjectBusy;
3248
3249     *mode = graphics->pixeloffset;
3250
3251     return Ok;
3252 }
3253
3254 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
3255 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
3256 {
3257     TRACE("(%p, %p)\n", graphics, mode);
3258
3259     if(!graphics || !mode)
3260         return InvalidParameter;
3261
3262     if(graphics->busy)
3263         return ObjectBusy;
3264
3265     *mode = graphics->smoothing;
3266
3267     return Ok;
3268 }
3269
3270 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
3271 {
3272     TRACE("(%p, %p)\n", graphics, contrast);
3273
3274     if(!graphics || !contrast)
3275         return InvalidParameter;
3276
3277     *contrast = graphics->textcontrast;
3278
3279     return Ok;
3280 }
3281
3282 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
3283 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
3284     TextRenderingHint *hint)
3285 {
3286     TRACE("(%p, %p)\n", graphics, hint);
3287
3288     if(!graphics || !hint)
3289         return InvalidParameter;
3290
3291     if(graphics->busy)
3292         return ObjectBusy;
3293
3294     *hint = graphics->texthint;
3295
3296     return Ok;
3297 }
3298
3299 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
3300 {
3301     GpRegion *clip_rgn;
3302     GpStatus stat;
3303
3304     TRACE("(%p, %p)\n", graphics, rect);
3305
3306     if(!graphics || !rect)
3307         return InvalidParameter;
3308
3309     if(graphics->busy)
3310         return ObjectBusy;
3311
3312     /* intersect window and graphics clipping regions */
3313     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
3314         return stat;
3315
3316     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
3317         goto cleanup;
3318
3319     /* get bounds of the region */
3320     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
3321
3322 cleanup:
3323     GdipDeleteRegion(clip_rgn);
3324
3325     return stat;
3326 }
3327
3328 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3329 {
3330     GpRectF rectf;
3331     GpStatus stat;
3332
3333     TRACE("(%p, %p)\n", graphics, rect);
3334
3335     if(!graphics || !rect)
3336         return InvalidParameter;
3337
3338     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
3339     {
3340         rect->X = roundr(rectf.X);
3341         rect->Y = roundr(rectf.Y);
3342         rect->Width  = roundr(rectf.Width);
3343         rect->Height = roundr(rectf.Height);
3344     }
3345
3346     return stat;
3347 }
3348
3349 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3350 {
3351     TRACE("(%p, %p)\n", graphics, matrix);
3352
3353     if(!graphics || !matrix)
3354         return InvalidParameter;
3355
3356     if(graphics->busy)
3357         return ObjectBusy;
3358
3359     *matrix = *graphics->worldtrans;
3360     return Ok;
3361 }
3362
3363 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
3364 {
3365     GpSolidFill *brush;
3366     GpStatus stat;
3367     GpRectF wnd_rect;
3368
3369     TRACE("(%p, %x)\n", graphics, color);
3370
3371     if(!graphics)
3372         return InvalidParameter;
3373
3374     if(graphics->busy)
3375         return ObjectBusy;
3376
3377     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3378         return stat;
3379
3380     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
3381         GdipDeleteBrush((GpBrush*)brush);
3382         return stat;
3383     }
3384
3385     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
3386                                                  wnd_rect.Width, wnd_rect.Height);
3387
3388     GdipDeleteBrush((GpBrush*)brush);
3389
3390     return Ok;
3391 }
3392
3393 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3394 {
3395     TRACE("(%p, %p)\n", graphics, res);
3396
3397     if(!graphics || !res)
3398         return InvalidParameter;
3399
3400     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3401 }
3402
3403 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3404 {
3405     GpStatus stat;
3406     GpRegion* rgn;
3407     GpPointF pt;
3408
3409     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
3410
3411     if(!graphics || !result)
3412         return InvalidParameter;
3413
3414     if(graphics->busy)
3415         return ObjectBusy;
3416
3417     pt.X = x;
3418     pt.Y = y;
3419     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3420                    CoordinateSpaceWorld, &pt, 1)) != Ok)
3421         return stat;
3422
3423     if((stat = GdipCreateRegion(&rgn)) != Ok)
3424         return stat;
3425
3426     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3427         goto cleanup;
3428
3429     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
3430
3431 cleanup:
3432     GdipDeleteRegion(rgn);
3433     return stat;
3434 }
3435
3436 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3437 {
3438     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
3439 }
3440
3441 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
3442 {
3443     GpStatus stat;
3444     GpRegion* rgn;
3445     GpPointF pts[2];
3446
3447     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
3448
3449     if(!graphics || !result)
3450         return InvalidParameter;
3451
3452     if(graphics->busy)
3453         return ObjectBusy;
3454
3455     pts[0].X = x;
3456     pts[0].Y = y;
3457     pts[1].X = x + width;
3458     pts[1].Y = y + height;
3459
3460     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3461                     CoordinateSpaceWorld, pts, 2)) != Ok)
3462         return stat;
3463
3464     pts[1].X -= pts[0].X;
3465     pts[1].Y -= pts[0].Y;
3466
3467     if((stat = GdipCreateRegion(&rgn)) != Ok)
3468         return stat;
3469
3470     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3471         goto cleanup;
3472
3473     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
3474
3475 cleanup:
3476     GdipDeleteRegion(rgn);
3477     return stat;
3478 }
3479
3480 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
3481 {
3482     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
3483 }
3484
3485 typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
3486     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3487     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3488     INT lineno, const RectF *bounds, void *user_data);
3489
3490 static GpStatus gdip_format_string(GpGraphics *graphics,
3491     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3492     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3493     gdip_format_string_callback callback, void *user_data)
3494 {
3495     WCHAR* stringdup;
3496     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
3497         nheight, lineend, lineno = 0;
3498     RectF bounds;
3499     StringAlignment halign;
3500     GpStatus stat = Ok;
3501     SIZE size;
3502
3503     if(length == -1) length = lstrlenW(string);
3504
3505     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
3506     if(!stringdup) return OutOfMemory;
3507
3508     nwidth = roundr(rect->Width);
3509     nheight = roundr(rect->Height);
3510
3511     if (nwidth == 0 || rect->Width >= INT_MAX) nwidth = INT_MAX;
3512     if (nheight == 0 || rect->Height >= INT_MAX) nheight = INT_MAX;
3513
3514     for(i = 0, j = 0; i < length; i++){
3515         /* FIXME: This makes the indexes passed to callback inaccurate. */
3516         if(!isprintW(string[i]) && (string[i] != '\n'))
3517             continue;
3518
3519         stringdup[j] = string[i];
3520         j++;
3521     }
3522
3523     length = j;
3524
3525     if (format) halign = format->align;
3526     else halign = StringAlignmentNear;
3527
3528     while(sum < length){
3529         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
3530                               nwidth, &fit, NULL, &size);
3531         fitcpy = fit;
3532
3533         if(fit == 0)
3534             break;
3535
3536         for(lret = 0; lret < fit; lret++)
3537             if(*(stringdup + sum + lret) == '\n')
3538                 break;
3539
3540         /* Line break code (may look strange, but it imitates windows). */
3541         if(lret < fit)
3542             lineend = fit = lret;    /* this is not an off-by-one error */
3543         else if(fit < (length - sum)){
3544             if(*(stringdup + sum + fit) == ' ')
3545                 while(*(stringdup + sum + fit) == ' ')
3546                     fit++;
3547             else
3548                 while(*(stringdup + sum + fit - 1) != ' '){
3549                     fit--;
3550
3551                     if(*(stringdup + sum + fit) == '\t')
3552                         break;
3553
3554                     if(fit == 0){
3555                         fit = fitcpy;
3556                         break;
3557                     }
3558                 }
3559             lineend = fit;
3560             while(*(stringdup + sum + lineend - 1) == ' ' ||
3561                   *(stringdup + sum + lineend - 1) == '\t')
3562                 lineend--;
3563         }
3564         else
3565             lineend = fit;
3566
3567         GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
3568                               nwidth, &j, NULL, &size);
3569
3570         bounds.Width = size.cx;
3571
3572         if(height + size.cy > nheight)
3573             bounds.Height = nheight - (height + size.cy);
3574         else
3575             bounds.Height = size.cy;
3576
3577         bounds.Y = rect->Y + height;
3578
3579         switch (halign)
3580         {
3581         case StringAlignmentNear:
3582         default:
3583             bounds.X = rect->X;
3584             break;
3585         case StringAlignmentCenter:
3586             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
3587             break;
3588         case StringAlignmentFar:
3589             bounds.X = rect->X + rect->Width - bounds.Width;
3590             break;
3591         }
3592
3593         stat = callback(graphics, stringdup, sum, lineend,
3594             font, rect, format, lineno, &bounds, user_data);
3595
3596         if (stat != Ok)
3597             break;
3598
3599         sum += fit + (lret < fitcpy ? 1 : 0);
3600         height += size.cy;
3601         lineno++;
3602
3603         if(height > nheight)
3604             break;
3605
3606         /* Stop if this was a linewrap (but not if it was a linebreak). */
3607         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3608             break;
3609     }
3610
3611     GdipFree(stringdup);
3612
3613     return stat;
3614 }
3615
3616 struct measure_ranges_args {
3617     GpRegion **regions;
3618 };
3619
3620 GpStatus measure_ranges_callback(GpGraphics *graphics,
3621     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3622     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3623     INT lineno, const RectF *bounds, void *user_data)
3624 {
3625     int i;
3626     GpStatus stat = Ok;
3627     struct measure_ranges_args *args = user_data;
3628
3629     for (i=0; i<format->range_count; i++)
3630     {
3631         INT range_start = max(index, format->character_ranges[i].First);
3632         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
3633         if (range_start < range_end)
3634         {
3635             GpRectF range_rect;
3636             SIZE range_size;
3637
3638             range_rect.Y = bounds->Y;
3639             range_rect.Height = bounds->Height;
3640
3641             GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
3642                                   INT_MAX, NULL, NULL, &range_size);
3643             range_rect.X = bounds->X + range_size.cx;
3644
3645             GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
3646                                   INT_MAX, NULL, NULL, &range_size);
3647             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
3648
3649             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
3650             if (stat != Ok)
3651                 break;
3652         }
3653     }
3654
3655     return stat;
3656 }
3657
3658 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
3659         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
3660         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
3661         INT regionCount, GpRegion** regions)
3662 {
3663     GpStatus stat;
3664     int i;
3665     HFONT oldfont;
3666     struct measure_ranges_args args;
3667
3668     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
3669             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
3670
3671     if (!(graphics && string && font && layoutRect && stringFormat && regions))
3672         return InvalidParameter;
3673
3674     if (regionCount < stringFormat->range_count)
3675         return InvalidParameter;
3676
3677     if (stringFormat->attr)
3678         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
3679
3680     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3681
3682     for (i=0; i<stringFormat->range_count; i++)
3683     {
3684         stat = GdipSetEmpty(regions[i]);
3685         if (stat != Ok)
3686             return stat;
3687     }
3688
3689     args.regions = regions;
3690
3691     stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
3692         measure_ranges_callback, &args);
3693
3694     DeleteObject(SelectObject(graphics->hdc, oldfont));
3695
3696     return stat;
3697 }
3698
3699 struct measure_string_args {
3700     RectF *bounds;
3701     INT *codepointsfitted;
3702     INT *linesfilled;
3703 };
3704
3705 static GpStatus measure_string_callback(GpGraphics *graphics,
3706     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3707     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3708     INT lineno, const RectF *bounds, void *user_data)
3709 {
3710     struct measure_string_args *args = user_data;
3711
3712     if (bounds->Width > args->bounds->Width)
3713         args->bounds->Width = bounds->Width;
3714
3715     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
3716         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
3717
3718     if (args->codepointsfitted)
3719         *args->codepointsfitted = index + length;
3720
3721     if (args->linesfilled)
3722         (*args->linesfilled)++;
3723
3724     return Ok;
3725 }
3726
3727 /* Find the smallest rectangle that bounds the text when it is printed in rect
3728  * according to the format options listed in format. If rect has 0 width and
3729  * height, then just find the smallest rectangle that bounds the text when it's
3730  * printed at location (rect->X, rect-Y). */
3731 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
3732     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3733     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
3734     INT *codepointsfitted, INT *linesfilled)
3735 {
3736     HFONT oldfont;
3737     struct measure_string_args args;
3738
3739     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
3740         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
3741         bounds, codepointsfitted, linesfilled);
3742
3743     if(!graphics || !string || !font || !rect || !bounds)
3744         return InvalidParameter;
3745
3746     if(linesfilled) *linesfilled = 0;
3747     if(codepointsfitted) *codepointsfitted = 0;
3748
3749     if(format)
3750         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3751
3752     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3753
3754     bounds->X = rect->X;
3755     bounds->Y = rect->Y;
3756     bounds->Width = 0.0;
3757     bounds->Height = 0.0;
3758
3759     args.bounds = bounds;
3760     args.codepointsfitted = codepointsfitted;
3761     args.linesfilled = linesfilled;
3762
3763     gdip_format_string(graphics, string, length, font, rect, format,
3764         measure_string_callback, &args);
3765
3766     DeleteObject(SelectObject(graphics->hdc, oldfont));
3767
3768     return Ok;
3769 }
3770
3771 struct draw_string_args {
3772     POINT drawbase;
3773     UINT drawflags;
3774     REAL ang_cos, ang_sin;
3775 };
3776
3777 static GpStatus draw_string_callback(GpGraphics *graphics,
3778     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3779     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3780     INT lineno, const RectF *bounds, void *user_data)
3781 {
3782     struct draw_string_args *args = user_data;
3783     RECT drawcoord;
3784
3785     drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
3786     drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
3787
3788     DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
3789
3790     return Ok;
3791 }
3792
3793 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
3794     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
3795     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
3796 {
3797     HRGN rgn = NULL;
3798     HFONT gdifont;
3799     LOGFONTW lfw;
3800     TEXTMETRICW textmet;
3801     GpPointF pt[3], rectcpy[4];
3802     POINT corners[4];
3803     REAL angle, rel_width, rel_height;
3804     INT offsety = 0, save_state;
3805     struct draw_string_args args;
3806     RectF scaled_rect;
3807
3808     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
3809         length, font, debugstr_rectf(rect), format, brush);
3810
3811     if(!graphics || !string || !font || !brush || !rect)
3812         return InvalidParameter;
3813
3814     if((brush->bt != BrushTypeSolidColor)){
3815         FIXME("not implemented for given parameters\n");
3816         return NotImplemented;
3817     }
3818
3819     if(format){
3820         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3821
3822         /* Should be no need to explicitly test for StringAlignmentNear as
3823          * that is default behavior if no alignment is passed. */
3824         if(format->vertalign != StringAlignmentNear){
3825             RectF bounds;
3826             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
3827
3828             if(format->vertalign == StringAlignmentCenter)
3829                 offsety = (rect->Height - bounds.Height) / 2;
3830             else if(format->vertalign == StringAlignmentFar)
3831                 offsety = (rect->Height - bounds.Height);
3832         }
3833     }
3834
3835     save_state = SaveDC(graphics->hdc);
3836     SetBkMode(graphics->hdc, TRANSPARENT);
3837     SetTextColor(graphics->hdc, brush->lb.lbColor);
3838
3839     pt[0].X = 0.0;
3840     pt[0].Y = 0.0;
3841     pt[1].X = 1.0;
3842     pt[1].Y = 0.0;
3843     pt[2].X = 0.0;
3844     pt[2].Y = 1.0;
3845     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
3846     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
3847     args.ang_cos = cos(angle);
3848     args.ang_sin = sin(angle);
3849     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
3850                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
3851     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
3852                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
3853
3854     rectcpy[3].X = rectcpy[0].X = rect->X;
3855     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
3856     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
3857     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
3858     transform_and_round_points(graphics, corners, rectcpy, 4);
3859
3860     scaled_rect.X = 0.0;
3861     scaled_rect.Y = 0.0;
3862     scaled_rect.Width = rel_width * rect->Width;
3863     scaled_rect.Height = rel_height * rect->Height;
3864
3865     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
3866     {
3867         /* FIXME: If only the width or only the height is 0, we should probably still clip */
3868         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
3869         SelectClipRgn(graphics->hdc, rgn);
3870     }
3871
3872     /* Use gdi to find the font, then perform transformations on it (height,
3873      * width, angle). */
3874     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3875     GetTextMetricsW(graphics->hdc, &textmet);
3876     lfw = font->lfw;
3877
3878     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
3879     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
3880
3881     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
3882
3883     gdifont = CreateFontIndirectW(&lfw);
3884     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
3885
3886     if (!format || format->align == StringAlignmentNear)
3887     {
3888         args.drawbase.x = corners[0].x;
3889         args.drawbase.y = corners[0].y;
3890         args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
3891     }
3892     else if (format->align == StringAlignmentCenter)
3893     {
3894         args.drawbase.x = (corners[0].x + corners[1].x)/2;
3895         args.drawbase.y = (corners[0].y + corners[1].y)/2;
3896         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
3897     }
3898     else /* (format->align == StringAlignmentFar) */
3899     {
3900         args.drawbase.x = corners[1].x;
3901         args.drawbase.y = corners[1].y;
3902         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
3903     }
3904
3905     gdip_format_string(graphics, string, length, font, &scaled_rect, format,
3906         draw_string_callback, &args);
3907
3908     DeleteObject(rgn);
3909     DeleteObject(gdifont);
3910
3911     RestoreDC(graphics->hdc, save_state);
3912
3913     return Ok;
3914 }
3915
3916 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
3917 {
3918     TRACE("(%p)\n", graphics);
3919
3920     if(!graphics)
3921         return InvalidParameter;
3922
3923     if(graphics->busy)
3924         return ObjectBusy;
3925
3926     return GdipSetInfinite(graphics->clip);
3927 }
3928
3929 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
3930 {
3931     TRACE("(%p)\n", graphics);
3932
3933     if(!graphics)
3934         return InvalidParameter;
3935
3936     if(graphics->busy)
3937         return ObjectBusy;
3938
3939     graphics->worldtrans->matrix[0] = 1.0;
3940     graphics->worldtrans->matrix[1] = 0.0;
3941     graphics->worldtrans->matrix[2] = 0.0;
3942     graphics->worldtrans->matrix[3] = 1.0;
3943     graphics->worldtrans->matrix[4] = 0.0;
3944     graphics->worldtrans->matrix[5] = 0.0;
3945
3946     return Ok;
3947 }
3948
3949 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
3950 {
3951     return GdipEndContainer(graphics, state);
3952 }
3953
3954 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
3955     GpMatrixOrder order)
3956 {
3957     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
3958
3959     if(!graphics)
3960         return InvalidParameter;
3961
3962     if(graphics->busy)
3963         return ObjectBusy;
3964
3965     return GdipRotateMatrix(graphics->worldtrans, angle, order);
3966 }
3967
3968 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
3969 {
3970     return GdipBeginContainer2(graphics, state);
3971 }
3972
3973 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
3974         GraphicsContainer *state)
3975 {
3976     GraphicsContainerItem *container;
3977     GpStatus sts;
3978
3979     TRACE("(%p, %p)\n", graphics, state);
3980
3981     if(!graphics || !state)
3982         return InvalidParameter;
3983
3984     sts = init_container(&container, graphics);
3985     if(sts != Ok)
3986         return sts;
3987
3988     list_add_head(&graphics->containers, &container->entry);
3989     *state = graphics->contid = container->contid;
3990
3991     return Ok;
3992 }
3993
3994 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
3995 {
3996     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
3997     return NotImplemented;
3998 }
3999
4000 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
4001 {
4002     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4003     return NotImplemented;
4004 }
4005
4006 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4007 {
4008     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4009     return NotImplemented;
4010 }
4011
4012 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4013 {
4014     GpStatus sts;
4015     GraphicsContainerItem *container, *container2;
4016
4017     TRACE("(%p, %x)\n", graphics, state);
4018
4019     if(!graphics)
4020         return InvalidParameter;
4021
4022     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4023         if(container->contid == state)
4024             break;
4025     }
4026
4027     /* did not find a matching container */
4028     if(&container->entry == &graphics->containers)
4029         return Ok;
4030
4031     sts = restore_container(graphics, container);
4032     if(sts != Ok)
4033         return sts;
4034
4035     /* remove all of the containers on top of the found container */
4036     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4037         if(container->contid == state)
4038             break;
4039         list_remove(&container->entry);
4040         delete_container(container);
4041     }
4042
4043     list_remove(&container->entry);
4044     delete_container(container);
4045
4046     return Ok;
4047 }
4048
4049 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4050     REAL sy, GpMatrixOrder order)
4051 {
4052     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4053
4054     if(!graphics)
4055         return InvalidParameter;
4056
4057     if(graphics->busy)
4058         return ObjectBusy;
4059
4060     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4061 }
4062
4063 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4064     CombineMode mode)
4065 {
4066     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4067
4068     if(!graphics || !srcgraphics)
4069         return InvalidParameter;
4070
4071     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4072 }
4073
4074 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4075     CompositingMode mode)
4076 {
4077     TRACE("(%p, %d)\n", graphics, mode);
4078
4079     if(!graphics)
4080         return InvalidParameter;
4081
4082     if(graphics->busy)
4083         return ObjectBusy;
4084
4085     graphics->compmode = mode;
4086
4087     return Ok;
4088 }
4089
4090 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4091     CompositingQuality quality)
4092 {
4093     TRACE("(%p, %d)\n", graphics, quality);
4094
4095     if(!graphics)
4096         return InvalidParameter;
4097
4098     if(graphics->busy)
4099         return ObjectBusy;
4100
4101     graphics->compqual = quality;
4102
4103     return Ok;
4104 }
4105
4106 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4107     InterpolationMode mode)
4108 {
4109     TRACE("(%p, %d)\n", graphics, mode);
4110
4111     if(!graphics)
4112         return InvalidParameter;
4113
4114     if(graphics->busy)
4115         return ObjectBusy;
4116
4117     graphics->interpolation = mode;
4118
4119     return Ok;
4120 }
4121
4122 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4123 {
4124     TRACE("(%p, %.2f)\n", graphics, scale);
4125
4126     if(!graphics || (scale <= 0.0))
4127         return InvalidParameter;
4128
4129     if(graphics->busy)
4130         return ObjectBusy;
4131
4132     graphics->scale = scale;
4133
4134     return Ok;
4135 }
4136
4137 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4138 {
4139     TRACE("(%p, %d)\n", graphics, unit);
4140
4141     if(!graphics)
4142         return InvalidParameter;
4143
4144     if(graphics->busy)
4145         return ObjectBusy;
4146
4147     if(unit == UnitWorld)
4148         return InvalidParameter;
4149
4150     graphics->unit = unit;
4151
4152     return Ok;
4153 }
4154
4155 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4156     mode)
4157 {
4158     TRACE("(%p, %d)\n", graphics, mode);
4159
4160     if(!graphics)
4161         return InvalidParameter;
4162
4163     if(graphics->busy)
4164         return ObjectBusy;
4165
4166     graphics->pixeloffset = mode;
4167
4168     return Ok;
4169 }
4170
4171 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
4172 {
4173     static int calls;
4174
4175     TRACE("(%p,%i,%i)\n", graphics, x, y);
4176
4177     if (!(calls++))
4178         FIXME("not implemented\n");
4179
4180     return NotImplemented;
4181 }
4182
4183 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
4184 {
4185     TRACE("(%p, %d)\n", graphics, mode);
4186
4187     if(!graphics)
4188         return InvalidParameter;
4189
4190     if(graphics->busy)
4191         return ObjectBusy;
4192
4193     graphics->smoothing = mode;
4194
4195     return Ok;
4196 }
4197
4198 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
4199 {
4200     TRACE("(%p, %d)\n", graphics, contrast);
4201
4202     if(!graphics)
4203         return InvalidParameter;
4204
4205     graphics->textcontrast = contrast;
4206
4207     return Ok;
4208 }
4209
4210 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
4211     TextRenderingHint hint)
4212 {
4213     TRACE("(%p, %d)\n", graphics, hint);
4214
4215     if(!graphics)
4216         return InvalidParameter;
4217
4218     if(graphics->busy)
4219         return ObjectBusy;
4220
4221     graphics->texthint = hint;
4222
4223     return Ok;
4224 }
4225
4226 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4227 {
4228     TRACE("(%p, %p)\n", graphics, matrix);
4229
4230     if(!graphics || !matrix)
4231         return InvalidParameter;
4232
4233     if(graphics->busy)
4234         return ObjectBusy;
4235
4236     GdipDeleteMatrix(graphics->worldtrans);
4237     return GdipCloneMatrix(matrix, &graphics->worldtrans);
4238 }
4239
4240 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
4241     REAL dy, GpMatrixOrder order)
4242 {
4243     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
4244
4245     if(!graphics)
4246         return InvalidParameter;
4247
4248     if(graphics->busy)
4249         return ObjectBusy;
4250
4251     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
4252 }
4253
4254 /*****************************************************************************
4255  * GdipSetClipHrgn [GDIPLUS.@]
4256  */
4257 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
4258 {
4259     GpRegion *region;
4260     GpStatus status;
4261
4262     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
4263
4264     if(!graphics)
4265         return InvalidParameter;
4266
4267     status = GdipCreateRegionHrgn(hrgn, &region);
4268     if(status != Ok)
4269         return status;
4270
4271     status = GdipSetClipRegion(graphics, region, mode);
4272
4273     GdipDeleteRegion(region);
4274     return status;
4275 }
4276
4277 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
4278 {
4279     TRACE("(%p, %p, %d)\n", graphics, path, mode);
4280
4281     if(!graphics)
4282         return InvalidParameter;
4283
4284     if(graphics->busy)
4285         return ObjectBusy;
4286
4287     return GdipCombineRegionPath(graphics->clip, path, mode);
4288 }
4289
4290 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
4291                                     REAL width, REAL height,
4292                                     CombineMode mode)
4293 {
4294     GpRectF rect;
4295
4296     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
4297
4298     if(!graphics)
4299         return InvalidParameter;
4300
4301     if(graphics->busy)
4302         return ObjectBusy;
4303
4304     rect.X = x;
4305     rect.Y = y;
4306     rect.Width  = width;
4307     rect.Height = height;
4308
4309     return GdipCombineRegionRect(graphics->clip, &rect, mode);
4310 }
4311
4312 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
4313                                      INT width, INT height,
4314                                      CombineMode mode)
4315 {
4316     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
4317
4318     if(!graphics)
4319         return InvalidParameter;
4320
4321     if(graphics->busy)
4322         return ObjectBusy;
4323
4324     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
4325 }
4326
4327 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
4328                                       CombineMode mode)
4329 {
4330     TRACE("(%p, %p, %d)\n", graphics, region, mode);
4331
4332     if(!graphics || !region)
4333         return InvalidParameter;
4334
4335     if(graphics->busy)
4336         return ObjectBusy;
4337
4338     return GdipCombineRegionRegion(graphics->clip, region, mode);
4339 }
4340
4341 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4342     UINT limitDpi)
4343 {
4344     static int calls;
4345
4346     TRACE("(%p,%u)\n", metafile, limitDpi);
4347
4348     if(!(calls++))
4349         FIXME("not implemented\n");
4350
4351     return NotImplemented;
4352 }
4353
4354 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
4355     INT count)
4356 {
4357     INT save_state;
4358     POINT *pti;
4359
4360     TRACE("(%p, %p, %d)\n", graphics, points, count);
4361
4362     if(!graphics || !pen || count<=0)
4363         return InvalidParameter;
4364
4365     if(graphics->busy)
4366         return ObjectBusy;
4367
4368     pti = GdipAlloc(sizeof(POINT) * count);
4369
4370     save_state = prepare_dc(graphics, pen);
4371     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
4372
4373     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
4374     Polygon(graphics->hdc, pti, count);
4375
4376     restore_dc(graphics, save_state);
4377     GdipFree(pti);
4378
4379     return Ok;
4380 }
4381
4382 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
4383     INT count)
4384 {
4385     GpStatus ret;
4386     GpPointF *ptf;
4387     INT i;
4388
4389     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
4390
4391     if(count<=0)    return InvalidParameter;
4392     ptf = GdipAlloc(sizeof(GpPointF) * count);
4393
4394     for(i = 0;i < count; i++){
4395         ptf[i].X = (REAL)points[i].X;
4396         ptf[i].Y = (REAL)points[i].Y;
4397     }
4398
4399     ret = GdipDrawPolygon(graphics,pen,ptf,count);
4400     GdipFree(ptf);
4401
4402     return ret;
4403 }
4404
4405 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
4406 {
4407     TRACE("(%p, %p)\n", graphics, dpi);
4408
4409     if(!graphics || !dpi)
4410         return InvalidParameter;
4411
4412     if(graphics->busy)
4413         return ObjectBusy;
4414
4415     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
4416
4417     return Ok;
4418 }
4419
4420 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
4421 {
4422     TRACE("(%p, %p)\n", graphics, dpi);
4423
4424     if(!graphics || !dpi)
4425         return InvalidParameter;
4426
4427     if(graphics->busy)
4428         return ObjectBusy;
4429
4430     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
4431
4432     return Ok;
4433 }
4434
4435 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
4436     GpMatrixOrder order)
4437 {
4438     GpMatrix m;
4439     GpStatus ret;
4440
4441     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
4442
4443     if(!graphics || !matrix)
4444         return InvalidParameter;
4445
4446     if(graphics->busy)
4447         return ObjectBusy;
4448
4449     m = *(graphics->worldtrans);
4450
4451     ret = GdipMultiplyMatrix(&m, matrix, order);
4452     if(ret == Ok)
4453         *(graphics->worldtrans) = m;
4454
4455     return ret;
4456 }
4457
4458 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
4459 {
4460     TRACE("(%p, %p)\n", graphics, hdc);
4461
4462     if(!graphics || !hdc)
4463         return InvalidParameter;
4464
4465     if(graphics->busy)
4466         return ObjectBusy;
4467
4468     *hdc = graphics->hdc;
4469     graphics->busy = TRUE;
4470
4471     return Ok;
4472 }
4473
4474 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
4475 {
4476     TRACE("(%p, %p)\n", graphics, hdc);
4477
4478     if(!graphics)
4479         return InvalidParameter;
4480
4481     if(graphics->hdc != hdc || !(graphics->busy))
4482         return InvalidParameter;
4483
4484     graphics->busy = FALSE;
4485
4486     return Ok;
4487 }
4488
4489 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
4490 {
4491     GpRegion *clip;
4492     GpStatus status;
4493
4494     TRACE("(%p, %p)\n", graphics, region);
4495
4496     if(!graphics || !region)
4497         return InvalidParameter;
4498
4499     if(graphics->busy)
4500         return ObjectBusy;
4501
4502     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
4503         return status;
4504
4505     /* free everything except root node and header */
4506     delete_element(&region->node);
4507     memcpy(region, clip, sizeof(GpRegion));
4508     GdipFree(clip);
4509
4510     return Ok;
4511 }
4512
4513 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
4514                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
4515 {
4516     GpMatrix *matrix;
4517     GpStatus stat;
4518     REAL unitscale;
4519
4520     if(!graphics || !points || count <= 0)
4521         return InvalidParameter;
4522
4523     if(graphics->busy)
4524         return ObjectBusy;
4525
4526     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4527
4528     if (src_space == dst_space) return Ok;
4529
4530     stat = GdipCreateMatrix(&matrix);
4531     if (stat == Ok)
4532     {
4533         unitscale = convert_unit(graphics->hdc, graphics->unit);
4534
4535         if(graphics->unit != UnitDisplay)
4536             unitscale *= graphics->scale;
4537
4538         /* transform from src_space to CoordinateSpacePage */
4539         switch (src_space)
4540         {
4541         case CoordinateSpaceWorld:
4542             GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
4543             break;
4544         case CoordinateSpacePage:
4545             break;
4546         case CoordinateSpaceDevice:
4547             GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
4548             break;
4549         }
4550
4551         /* transform from CoordinateSpacePage to dst_space */
4552         switch (dst_space)
4553         {
4554         case CoordinateSpaceWorld:
4555             {
4556                 GpMatrix *inverted_transform;
4557                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
4558                 if (stat == Ok)
4559                 {
4560                     stat = GdipInvertMatrix(inverted_transform);
4561                     if (stat == Ok)
4562                         GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
4563                     GdipDeleteMatrix(inverted_transform);
4564                 }
4565                 break;
4566             }
4567         case CoordinateSpacePage:
4568             break;
4569         case CoordinateSpaceDevice:
4570             GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
4571             break;
4572         }
4573
4574         if (stat == Ok)
4575             stat = GdipTransformMatrixPoints(matrix, points, count);
4576
4577         GdipDeleteMatrix(matrix);
4578     }
4579
4580     return stat;
4581 }
4582
4583 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
4584                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
4585 {
4586     GpPointF *pointsF;
4587     GpStatus ret;
4588     INT i;
4589
4590     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4591
4592     if(count <= 0)
4593         return InvalidParameter;
4594
4595     pointsF = GdipAlloc(sizeof(GpPointF) * count);
4596     if(!pointsF)
4597         return OutOfMemory;
4598
4599     for(i = 0; i < count; i++){
4600         pointsF[i].X = (REAL)points[i].X;
4601         pointsF[i].Y = (REAL)points[i].Y;
4602     }
4603
4604     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
4605
4606     if(ret == Ok)
4607         for(i = 0; i < count; i++){
4608             points[i].X = roundr(pointsF[i].X);
4609             points[i].Y = roundr(pointsF[i].Y);
4610         }
4611     GdipFree(pointsF);
4612
4613     return ret;
4614 }
4615
4616 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
4617 {
4618     static int calls;
4619
4620     TRACE("\n");
4621
4622     if (!calls++)
4623       FIXME("stub\n");
4624
4625     return NULL;
4626 }
4627
4628 /*****************************************************************************
4629  * GdipTranslateClip [GDIPLUS.@]
4630  */
4631 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
4632 {
4633     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
4634
4635     if(!graphics)
4636         return InvalidParameter;
4637
4638     if(graphics->busy)
4639         return ObjectBusy;
4640
4641     return GdipTranslateRegion(graphics->clip, dx, dy);
4642 }
4643
4644 /*****************************************************************************
4645  * GdipTranslateClipI [GDIPLUS.@]
4646  */
4647 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
4648 {
4649     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
4650
4651     if(!graphics)
4652         return InvalidParameter;
4653
4654     if(graphics->busy)
4655         return ObjectBusy;
4656
4657     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
4658 }
4659
4660
4661 /*****************************************************************************
4662  * GdipMeasureDriverString [GDIPLUS.@]
4663  */
4664 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4665                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
4666                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
4667 {
4668     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
4669     return NotImplemented;
4670 }
4671
4672 /*****************************************************************************
4673  * GdipDrawDriverString [GDIPLUS.@]
4674  */
4675 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4676                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
4677                                          GDIPCONST PointF *positions, INT flags,
4678                                          GDIPCONST GpMatrix *matrix )
4679 {
4680     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
4681     return NotImplemented;
4682 }
4683
4684 /*****************************************************************************
4685  * GdipRecordMetafileI [GDIPLUS.@]
4686  */
4687 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4688                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4689 {
4690     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4691     return NotImplemented;
4692 }
4693
4694 /*****************************************************************************
4695  * GdipIsVisibleClipEmpty [GDIPLUS.@]
4696  */
4697 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
4698 {
4699     GpStatus stat;
4700     GpRegion* rgn;
4701
4702     TRACE("(%p, %p)\n", graphics, res);
4703
4704     if((stat = GdipCreateRegion(&rgn)) != Ok)
4705         return stat;
4706
4707     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4708         goto cleanup;
4709
4710     stat = GdipIsEmptyRegion(rgn, graphics, res);
4711
4712 cleanup:
4713     GdipDeleteRegion(rgn);
4714     return stat;
4715 }