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