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