wined3d: Merge the various resource desc structures.
[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 (count > 3)
2165         return NotImplemented;
2166
2167     if(!graphics || !image || !points || count != 3)
2168          return InvalidParameter;
2169
2170     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2171         debugstr_pointf(&points[2]));
2172
2173     memcpy(ptf, points, 3 * sizeof(GpPointF));
2174     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
2175     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2176     if (!srcwidth || !srcheight || ptf[3].X == ptf[0].X || ptf[3].Y == ptf[0].Y)
2177         return Ok;
2178     transform_and_round_points(graphics, pti, ptf, 4);
2179
2180     if (image->picture)
2181     {
2182         if (!graphics->hdc)
2183         {
2184             FIXME("graphics object has no HDC\n");
2185         }
2186
2187         /* FIXME: partially implemented (only works for rectangular parallelograms) */
2188         if(srcUnit == UnitInch)
2189             dx = dy = (REAL) INCH_HIMETRIC;
2190         else if(srcUnit == UnitPixel){
2191             dx = ((REAL) INCH_HIMETRIC) /
2192                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
2193             dy = ((REAL) INCH_HIMETRIC) /
2194                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
2195         }
2196         else
2197             return NotImplemented;
2198
2199         if(IPicture_Render(image->picture, graphics->hdc,
2200             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
2201             srcx * dx, srcy * dy,
2202             srcwidth * dx, srcheight * dy,
2203             NULL) != S_OK){
2204             if(callback)
2205                 callback(callbackData);
2206             return GenericError;
2207         }
2208     }
2209     else if (image->type == ImageTypeBitmap)
2210     {
2211         GpBitmap* bitmap = (GpBitmap*)image;
2212         int use_software=0;
2213
2214         if (srcUnit == UnitInch)
2215             dx = dy = 96.0; /* FIXME: use the image resolution */
2216         else if (srcUnit == UnitPixel)
2217             dx = dy = 1.0;
2218         else
2219             return NotImplemented;
2220
2221         if (imageAttributes ||
2222             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
2223             !((GpBitmap*)image)->hbitmap ||
2224             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X)
2225             use_software = 1;
2226
2227         if (use_software)
2228         {
2229             RECT src_area, dst_area;
2230             int i, x, y, stride;
2231             GpMatrix *dst_to_src;
2232             REAL m11, m12, m21, m22, mdx, mdy;
2233             LPBYTE data;
2234
2235             src_area.left = srcx*dx;
2236             src_area.top = srcy*dy;
2237             src_area.right = (srcx+srcwidth)*dx;
2238             src_area.bottom = (srcy+srcheight)*dy;
2239
2240             dst_area.left = dst_area.right = pti[0].x;
2241             dst_area.top = dst_area.bottom = pti[0].y;
2242             for (i=1; i<4; i++)
2243             {
2244                 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2245                 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2246                 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2247                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2248             }
2249
2250             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2251             m21 = (ptf[2].X - ptf[0].X) / srcheight;
2252             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2253             m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2254             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2255             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2256
2257             stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
2258             if (stat != Ok) return stat;
2259
2260             stat = GdipInvertMatrix(dst_to_src);
2261             if (stat != Ok)
2262             {
2263                 GdipDeleteMatrix(dst_to_src);
2264                 return stat;
2265             }
2266
2267             data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
2268             if (!data)
2269             {
2270                 GdipDeleteMatrix(dst_to_src);
2271                 return OutOfMemory;
2272             }
2273
2274             stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
2275
2276             if (imageAttributes &&
2277                 (imageAttributes->wrap != WrapModeClamp ||
2278                  imageAttributes->outside_color != 0x00000000 ||
2279                  imageAttributes->clamp))
2280             {
2281                 static int fixme;
2282                 if (!fixme++)
2283                     FIXME("Image wrap mode not implemented\n");
2284             }
2285
2286             for (x=dst_area.left; x<dst_area.right; x++)
2287             {
2288                 for (y=dst_area.top; y<dst_area.bottom; y++)
2289                 {
2290                     GpPointF src_pointf;
2291                     int src_x, src_y;
2292                     ARGB *src_color;
2293
2294                     src_pointf.X = x;
2295                     src_pointf.Y = y;
2296
2297                     GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
2298
2299                     src_x = roundr(src_pointf.X);
2300                     src_y = roundr(src_pointf.Y);
2301
2302                     src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2303
2304                     if (src_x < src_area.left || src_x >= src_area.right ||
2305                         src_y < src_area.top || src_y >= src_area.bottom)
2306                         *src_color = 0;
2307                     else
2308                         GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
2309                 }
2310             }
2311
2312             GdipDeleteMatrix(dst_to_src);
2313
2314             if (imageAttributes)
2315             {
2316                 if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
2317                     imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
2318                 {
2319                     const struct color_key *key;
2320                     BYTE min_blue, min_green, min_red;
2321                     BYTE max_blue, max_green, max_red;
2322
2323                     if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled)
2324                         key = &imageAttributes->colorkeys[ColorAdjustTypeBitmap];
2325                     else
2326                         key = &imageAttributes->colorkeys[ColorAdjustTypeDefault];
2327
2328                     min_blue = key->low&0xff;
2329                     min_green = (key->low>>8)&0xff;
2330                     min_red = (key->low>>16)&0xff;
2331
2332                     max_blue = key->high&0xff;
2333                     max_green = (key->high>>8)&0xff;
2334                     max_red = (key->high>>16)&0xff;
2335
2336                     for (x=dst_area.left; x<dst_area.right; x++)
2337                         for (y=dst_area.top; y<dst_area.bottom; y++)
2338                         {
2339                             ARGB *src_color;
2340                             BYTE blue, green, red;
2341                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2342                             blue = *src_color&0xff;
2343                             green = (*src_color>>8)&0xff;
2344                             red = (*src_color>>16)&0xff;
2345                             if (blue >= min_blue && green >= min_green && red >= min_red &&
2346                                 blue <= max_blue && green <= max_green && red <= max_red)
2347                                 *src_color = 0x00000000;
2348                         }
2349                 }
2350
2351                 if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
2352                     imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
2353                 {
2354                     const struct color_remap_table *table;
2355
2356                     if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
2357                         table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
2358                     else
2359                         table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
2360
2361                     for (x=dst_area.left; x<dst_area.right; x++)
2362                         for (y=dst_area.top; y<dst_area.bottom; y++)
2363                         {
2364                             ARGB *src_color;
2365                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2366                             for (i=0; i<table->mapsize; i++)
2367                             {
2368                                 if (*src_color == table->colormap[i].oldColor.Argb)
2369                                 {
2370                                     *src_color = table->colormap[i].newColor.Argb;
2371                                     break;
2372                                 }
2373                             }
2374                         }
2375                 }
2376
2377                 if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
2378                     imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
2379                 {
2380                     static int fixme;
2381                     if (!fixme++)
2382                         FIXME("Color transforms not implemented\n");
2383                 }
2384
2385                 if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
2386                     imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
2387                 {
2388                     static int fixme;
2389                     if (!fixme++)
2390                         FIXME("Gamma adjustment not implemented\n");
2391                 }
2392             }
2393
2394             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
2395                 data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
2396
2397             GdipFree(data);
2398
2399             return stat;
2400         }
2401         else
2402         {
2403             HDC hdc;
2404             int temp_hdc=0, temp_bitmap=0;
2405             HBITMAP hbitmap, old_hbm=NULL;
2406
2407             if (!(bitmap->format == PixelFormat16bppRGB555 ||
2408                   bitmap->format == PixelFormat24bppRGB ||
2409                   bitmap->format == PixelFormat32bppRGB ||
2410                   bitmap->format == PixelFormat32bppPARGB))
2411             {
2412                 BITMAPINFOHEADER bih;
2413                 BYTE *temp_bits;
2414                 PixelFormat dst_format;
2415
2416                 /* we can't draw a bitmap of this format directly */
2417                 hdc = CreateCompatibleDC(0);
2418                 temp_hdc = 1;
2419                 temp_bitmap = 1;
2420
2421                 bih.biSize = sizeof(BITMAPINFOHEADER);
2422                 bih.biWidth = bitmap->width;
2423                 bih.biHeight = -bitmap->height;
2424                 bih.biPlanes = 1;
2425                 bih.biBitCount = 32;
2426                 bih.biCompression = BI_RGB;
2427                 bih.biSizeImage = 0;
2428                 bih.biXPelsPerMeter = 0;
2429                 bih.biYPelsPerMeter = 0;
2430                 bih.biClrUsed = 0;
2431                 bih.biClrImportant = 0;
2432
2433                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
2434                     (void**)&temp_bits, NULL, 0);
2435
2436                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2437                     dst_format = PixelFormat32bppPARGB;
2438                 else
2439                     dst_format = PixelFormat32bppRGB;
2440
2441                 convert_pixels(bitmap->width, bitmap->height,
2442                     bitmap->width*4, temp_bits, dst_format,
2443                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
2444             }
2445             else
2446             {
2447                 hbitmap = bitmap->hbitmap;
2448                 hdc = bitmap->hdc;
2449                 temp_hdc = (hdc == 0);
2450             }
2451
2452             if (temp_hdc)
2453             {
2454                 if (!hdc) hdc = CreateCompatibleDC(0);
2455                 old_hbm = SelectObject(hdc, hbitmap);
2456             }
2457
2458             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2459             {
2460                 BLENDFUNCTION bf;
2461
2462                 bf.BlendOp = AC_SRC_OVER;
2463                 bf.BlendFlags = 0;
2464                 bf.SourceConstantAlpha = 255;
2465                 bf.AlphaFormat = AC_SRC_ALPHA;
2466
2467                 GdiAlphaBlend(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, bf);
2469             }
2470             else
2471             {
2472                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2473                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
2474             }
2475
2476             if (temp_hdc)
2477             {
2478                 SelectObject(hdc, old_hbm);
2479                 DeleteDC(hdc);
2480             }
2481
2482             if (temp_bitmap)
2483                 DeleteObject(hbitmap);
2484         }
2485     }
2486     else
2487     {
2488         ERR("GpImage with no IPicture or HBITMAP?!\n");
2489         return NotImplemented;
2490     }
2491
2492     return Ok;
2493 }
2494
2495 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
2496      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
2497      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2498      DrawImageAbort callback, VOID * callbackData)
2499 {
2500     GpPointF pointsF[3];
2501     INT i;
2502
2503     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
2504           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2505           callbackData);
2506
2507     if(!points || count!=3)
2508         return InvalidParameter;
2509
2510     for(i = 0; i < count; i++){
2511         pointsF[i].X = (REAL)points[i].X;
2512         pointsF[i].Y = (REAL)points[i].Y;
2513     }
2514
2515     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
2516                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
2517                                    callback, callbackData);
2518 }
2519
2520 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
2521     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
2522     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
2523     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
2524     VOID * callbackData)
2525 {
2526     GpPointF points[3];
2527
2528     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
2529           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2530           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2531
2532     points[0].X = dstx;
2533     points[0].Y = dsty;
2534     points[1].X = dstx + dstwidth;
2535     points[1].Y = dsty;
2536     points[2].X = dstx;
2537     points[2].Y = dsty + dstheight;
2538
2539     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2540                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2541 }
2542
2543 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
2544         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
2545         INT srcwidth, INT srcheight, GpUnit srcUnit,
2546         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
2547         VOID * callbackData)
2548 {
2549     GpPointF points[3];
2550
2551     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
2552           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2553           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2554
2555     points[0].X = dstx;
2556     points[0].Y = dsty;
2557     points[1].X = dstx + dstwidth;
2558     points[1].Y = dsty;
2559     points[2].X = dstx;
2560     points[2].Y = dsty + dstheight;
2561
2562     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2563                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2564 }
2565
2566 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
2567     REAL x, REAL y, REAL width, REAL height)
2568 {
2569     RectF bounds;
2570     GpUnit unit;
2571     GpStatus ret;
2572
2573     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
2574
2575     if(!graphics || !image)
2576         return InvalidParameter;
2577
2578     ret = GdipGetImageBounds(image, &bounds, &unit);
2579     if(ret != Ok)
2580         return ret;
2581
2582     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
2583                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
2584                                  unit, NULL, NULL, NULL);
2585 }
2586
2587 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
2588     INT x, INT y, INT width, INT height)
2589 {
2590     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
2591
2592     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
2593 }
2594
2595 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
2596     REAL y1, REAL x2, REAL y2)
2597 {
2598     INT save_state;
2599     GpPointF pt[2];
2600     GpStatus retval;
2601
2602     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
2603
2604     if(!pen || !graphics)
2605         return InvalidParameter;
2606
2607     if(graphics->busy)
2608         return ObjectBusy;
2609
2610     if (!graphics->hdc)
2611     {
2612         FIXME("graphics object has no HDC\n");
2613         return Ok;
2614     }
2615
2616     pt[0].X = x1;
2617     pt[0].Y = y1;
2618     pt[1].X = x2;
2619     pt[1].Y = y2;
2620
2621     save_state = prepare_dc(graphics, pen);
2622
2623     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2624
2625     restore_dc(graphics, save_state);
2626
2627     return retval;
2628 }
2629
2630 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
2631     INT y1, INT x2, INT y2)
2632 {
2633     INT save_state;
2634     GpPointF pt[2];
2635     GpStatus retval;
2636
2637     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
2638
2639     if(!pen || !graphics)
2640         return InvalidParameter;
2641
2642     if(graphics->busy)
2643         return ObjectBusy;
2644
2645     if (!graphics->hdc)
2646     {
2647         FIXME("graphics object has no HDC\n");
2648         return Ok;
2649     }
2650
2651     pt[0].X = (REAL)x1;
2652     pt[0].Y = (REAL)y1;
2653     pt[1].X = (REAL)x2;
2654     pt[1].Y = (REAL)y2;
2655
2656     save_state = prepare_dc(graphics, pen);
2657
2658     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2659
2660     restore_dc(graphics, save_state);
2661
2662     return retval;
2663 }
2664
2665 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
2666     GpPointF *points, INT count)
2667 {
2668     INT save_state;
2669     GpStatus retval;
2670
2671     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2672
2673     if(!pen || !graphics || (count < 2))
2674         return InvalidParameter;
2675
2676     if(graphics->busy)
2677         return ObjectBusy;
2678
2679     if (!graphics->hdc)
2680     {
2681         FIXME("graphics object has no HDC\n");
2682         return Ok;
2683     }
2684
2685     save_state = prepare_dc(graphics, pen);
2686
2687     retval = draw_polyline(graphics, pen, points, count, TRUE);
2688
2689     restore_dc(graphics, save_state);
2690
2691     return retval;
2692 }
2693
2694 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
2695     GpPoint *points, INT count)
2696 {
2697     INT save_state;
2698     GpStatus retval;
2699     GpPointF *ptf = NULL;
2700     int i;
2701
2702     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2703
2704     if(!pen || !graphics || (count < 2))
2705         return InvalidParameter;
2706
2707     if(graphics->busy)
2708         return ObjectBusy;
2709
2710     if (!graphics->hdc)
2711     {
2712         FIXME("graphics object has no HDC\n");
2713         return Ok;
2714     }
2715
2716     ptf = GdipAlloc(count * sizeof(GpPointF));
2717     if(!ptf) return OutOfMemory;
2718
2719     for(i = 0; i < count; i ++){
2720         ptf[i].X = (REAL) points[i].X;
2721         ptf[i].Y = (REAL) points[i].Y;
2722     }
2723
2724     save_state = prepare_dc(graphics, pen);
2725
2726     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
2727
2728     restore_dc(graphics, save_state);
2729
2730     GdipFree(ptf);
2731     return retval;
2732 }
2733
2734 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
2735 {
2736     INT save_state;
2737     GpStatus retval;
2738
2739     TRACE("(%p, %p, %p)\n", graphics, pen, path);
2740
2741     if(!pen || !graphics)
2742         return InvalidParameter;
2743
2744     if(graphics->busy)
2745         return ObjectBusy;
2746
2747     if (!graphics->hdc)
2748     {
2749         FIXME("graphics object has no HDC\n");
2750         return Ok;
2751     }
2752
2753     save_state = prepare_dc(graphics, pen);
2754
2755     retval = draw_poly(graphics, pen, path->pathdata.Points,
2756                        path->pathdata.Types, path->pathdata.Count, TRUE);
2757
2758     restore_dc(graphics, save_state);
2759
2760     return retval;
2761 }
2762
2763 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
2764     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2765 {
2766     INT save_state;
2767
2768     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2769             width, height, startAngle, sweepAngle);
2770
2771     if(!graphics || !pen)
2772         return InvalidParameter;
2773
2774     if(graphics->busy)
2775         return ObjectBusy;
2776
2777     if (!graphics->hdc)
2778     {
2779         FIXME("graphics object has no HDC\n");
2780         return Ok;
2781     }
2782
2783     save_state = prepare_dc(graphics, pen);
2784     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2785
2786     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2787
2788     restore_dc(graphics, save_state);
2789
2790     return Ok;
2791 }
2792
2793 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
2794     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2795 {
2796     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2797             width, height, startAngle, sweepAngle);
2798
2799     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2800 }
2801
2802 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
2803     REAL y, REAL width, REAL height)
2804 {
2805     INT save_state;
2806     GpPointF ptf[4];
2807     POINT pti[4];
2808
2809     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2810
2811     if(!pen || !graphics)
2812         return InvalidParameter;
2813
2814     if(graphics->busy)
2815         return ObjectBusy;
2816
2817     if (!graphics->hdc)
2818     {
2819         FIXME("graphics object has no HDC\n");
2820         return Ok;
2821     }
2822
2823     ptf[0].X = x;
2824     ptf[0].Y = y;
2825     ptf[1].X = x + width;
2826     ptf[1].Y = y;
2827     ptf[2].X = x + width;
2828     ptf[2].Y = y + height;
2829     ptf[3].X = x;
2830     ptf[3].Y = y + height;
2831
2832     save_state = prepare_dc(graphics, pen);
2833     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2834
2835     transform_and_round_points(graphics, pti, ptf, 4);
2836     Polygon(graphics->hdc, pti, 4);
2837
2838     restore_dc(graphics, save_state);
2839
2840     return Ok;
2841 }
2842
2843 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
2844     INT y, INT width, INT height)
2845 {
2846     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2847
2848     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2849 }
2850
2851 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
2852     GDIPCONST GpRectF* rects, INT count)
2853 {
2854     GpPointF *ptf;
2855     POINT *pti;
2856     INT save_state, i;
2857
2858     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2859
2860     if(!graphics || !pen || !rects || count < 1)
2861         return InvalidParameter;
2862
2863     if(graphics->busy)
2864         return ObjectBusy;
2865
2866     if (!graphics->hdc)
2867     {
2868         FIXME("graphics object has no HDC\n");
2869         return Ok;
2870     }
2871
2872     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
2873     pti = GdipAlloc(4 * count * sizeof(POINT));
2874
2875     if(!ptf || !pti){
2876         GdipFree(ptf);
2877         GdipFree(pti);
2878         return OutOfMemory;
2879     }
2880
2881     for(i = 0; i < count; i++){
2882         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
2883         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
2884         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
2885         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
2886     }
2887
2888     save_state = prepare_dc(graphics, pen);
2889     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2890
2891     transform_and_round_points(graphics, pti, ptf, 4 * count);
2892
2893     for(i = 0; i < count; i++)
2894         Polygon(graphics->hdc, &pti[4 * i], 4);
2895
2896     restore_dc(graphics, save_state);
2897
2898     GdipFree(ptf);
2899     GdipFree(pti);
2900
2901     return Ok;
2902 }
2903
2904 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
2905     GDIPCONST GpRect* rects, INT count)
2906 {
2907     GpRectF *rectsF;
2908     GpStatus ret;
2909     INT i;
2910
2911     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2912
2913     if(!rects || count<=0)
2914         return InvalidParameter;
2915
2916     rectsF = GdipAlloc(sizeof(GpRectF) * count);
2917     if(!rectsF)
2918         return OutOfMemory;
2919
2920     for(i = 0;i < count;i++){
2921         rectsF[i].X      = (REAL)rects[i].X;
2922         rectsF[i].Y      = (REAL)rects[i].Y;
2923         rectsF[i].Width  = (REAL)rects[i].Width;
2924         rectsF[i].Height = (REAL)rects[i].Height;
2925     }
2926
2927     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
2928     GdipFree(rectsF);
2929
2930     return ret;
2931 }
2932
2933 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2934     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2935 {
2936     GpPath *path;
2937     GpStatus stat;
2938
2939     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2940             count, tension, fill);
2941
2942     if(!graphics || !brush || !points)
2943         return InvalidParameter;
2944
2945     if(graphics->busy)
2946         return ObjectBusy;
2947
2948     if(count == 1)    /* Do nothing */
2949         return Ok;
2950
2951     stat = GdipCreatePath(fill, &path);
2952     if(stat != Ok)
2953         return stat;
2954
2955     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2956     if(stat != Ok){
2957         GdipDeletePath(path);
2958         return stat;
2959     }
2960
2961     stat = GdipFillPath(graphics, brush, path);
2962     if(stat != Ok){
2963         GdipDeletePath(path);
2964         return stat;
2965     }
2966
2967     GdipDeletePath(path);
2968
2969     return Ok;
2970 }
2971
2972 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2973     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2974 {
2975     GpPointF *ptf;
2976     GpStatus stat;
2977     INT i;
2978
2979     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2980             count, tension, fill);
2981
2982     if(!points || count == 0)
2983         return InvalidParameter;
2984
2985     if(count == 1)    /* Do nothing */
2986         return Ok;
2987
2988     ptf = GdipAlloc(sizeof(GpPointF)*count);
2989     if(!ptf)
2990         return OutOfMemory;
2991
2992     for(i = 0;i < count;i++){
2993         ptf[i].X = (REAL)points[i].X;
2994         ptf[i].Y = (REAL)points[i].Y;
2995     }
2996
2997     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2998
2999     GdipFree(ptf);
3000
3001     return stat;
3002 }
3003
3004 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
3005     GDIPCONST GpPointF *points, INT count)
3006 {
3007     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3008     return GdipFillClosedCurve2(graphics, brush, points, count,
3009                0.5f, FillModeAlternate);
3010 }
3011
3012 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
3013     GDIPCONST GpPoint *points, INT count)
3014 {
3015     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3016     return GdipFillClosedCurve2I(graphics, brush, points, count,
3017                0.5f, FillModeAlternate);
3018 }
3019
3020 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
3021     REAL y, REAL width, REAL height)
3022 {
3023     INT save_state;
3024     GpPointF ptf[2];
3025     POINT pti[2];
3026
3027     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3028
3029     if(!graphics || !brush)
3030         return InvalidParameter;
3031
3032     if(graphics->busy)
3033         return ObjectBusy;
3034
3035     if(!graphics->hdc)
3036     {
3037         FIXME("graphics object has no HDC\n");
3038         return Ok;
3039     }
3040
3041     ptf[0].X = x;
3042     ptf[0].Y = y;
3043     ptf[1].X = x + width;
3044     ptf[1].Y = y + height;
3045
3046     save_state = SaveDC(graphics->hdc);
3047     EndPath(graphics->hdc);
3048
3049     transform_and_round_points(graphics, pti, ptf, 2);
3050
3051     BeginPath(graphics->hdc);
3052     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
3053     EndPath(graphics->hdc);
3054
3055     brush_fill_path(graphics, brush);
3056
3057     RestoreDC(graphics->hdc, save_state);
3058
3059     return Ok;
3060 }
3061
3062 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
3063     INT y, INT width, INT height)
3064 {
3065     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3066
3067     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3068 }
3069
3070 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3071 {
3072     INT save_state;
3073     GpStatus retval;
3074
3075     TRACE("(%p, %p, %p)\n", graphics, brush, path);
3076
3077     if(!brush || !graphics || !path)
3078         return InvalidParameter;
3079
3080     if(graphics->busy)
3081         return ObjectBusy;
3082
3083     if(!graphics->hdc)
3084     {
3085         FIXME("graphics object has no HDC\n");
3086         return Ok;
3087     }
3088
3089     save_state = SaveDC(graphics->hdc);
3090     EndPath(graphics->hdc);
3091     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
3092                                                                     : WINDING));
3093
3094     BeginPath(graphics->hdc);
3095     retval = draw_poly(graphics, NULL, path->pathdata.Points,
3096                        path->pathdata.Types, path->pathdata.Count, FALSE);
3097
3098     if(retval != Ok)
3099         goto end;
3100
3101     EndPath(graphics->hdc);
3102     brush_fill_path(graphics, brush);
3103
3104     retval = Ok;
3105
3106 end:
3107     RestoreDC(graphics->hdc, save_state);
3108
3109     return retval;
3110 }
3111
3112 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
3113     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3114 {
3115     INT save_state;
3116
3117     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
3118             graphics, brush, x, y, width, height, startAngle, sweepAngle);
3119
3120     if(!graphics || !brush)
3121         return InvalidParameter;
3122
3123     if(graphics->busy)
3124         return ObjectBusy;
3125
3126     if(!graphics->hdc)
3127     {
3128         FIXME("graphics object has no HDC\n");
3129         return Ok;
3130     }
3131
3132     save_state = SaveDC(graphics->hdc);
3133     EndPath(graphics->hdc);
3134
3135     BeginPath(graphics->hdc);
3136     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
3137     EndPath(graphics->hdc);
3138
3139     brush_fill_path(graphics, brush);
3140
3141     RestoreDC(graphics->hdc, save_state);
3142
3143     return Ok;
3144 }
3145
3146 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
3147     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3148 {
3149     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
3150             graphics, brush, x, y, width, height, startAngle, sweepAngle);
3151
3152     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3153 }
3154
3155 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
3156     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
3157 {
3158     INT save_state;
3159     GpPointF *ptf = NULL;
3160     POINT *pti = NULL;
3161     GpStatus retval = Ok;
3162
3163     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3164
3165     if(!graphics || !brush || !points || !count)
3166         return InvalidParameter;
3167
3168     if(graphics->busy)
3169         return ObjectBusy;
3170
3171     if(!graphics->hdc)
3172     {
3173         FIXME("graphics object has no HDC\n");
3174         return Ok;
3175     }
3176
3177     ptf = GdipAlloc(count * sizeof(GpPointF));
3178     pti = GdipAlloc(count * sizeof(POINT));
3179     if(!ptf || !pti){
3180         retval = OutOfMemory;
3181         goto end;
3182     }
3183
3184     memcpy(ptf, points, count * sizeof(GpPointF));
3185
3186     save_state = SaveDC(graphics->hdc);
3187     EndPath(graphics->hdc);
3188     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
3189                                                                   : WINDING));
3190
3191     transform_and_round_points(graphics, pti, ptf, count);
3192
3193     BeginPath(graphics->hdc);
3194     Polygon(graphics->hdc, pti, count);
3195     EndPath(graphics->hdc);
3196
3197     brush_fill_path(graphics, brush);
3198
3199     RestoreDC(graphics->hdc, save_state);
3200
3201 end:
3202     GdipFree(ptf);
3203     GdipFree(pti);
3204
3205     return retval;
3206 }
3207
3208 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
3209     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
3210 {
3211     INT save_state, i;
3212     GpPointF *ptf = NULL;
3213     POINT *pti = NULL;
3214     GpStatus retval = Ok;
3215
3216     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3217
3218     if(!graphics || !brush || !points || !count)
3219         return InvalidParameter;
3220
3221     if(graphics->busy)
3222         return ObjectBusy;
3223
3224     if(!graphics->hdc)
3225     {
3226         FIXME("graphics object has no HDC\n");
3227         return Ok;
3228     }
3229
3230     ptf = GdipAlloc(count * sizeof(GpPointF));
3231     pti = GdipAlloc(count * sizeof(POINT));
3232     if(!ptf || !pti){
3233         retval = OutOfMemory;
3234         goto end;
3235     }
3236
3237     for(i = 0; i < count; i ++){
3238         ptf[i].X = (REAL) points[i].X;
3239         ptf[i].Y = (REAL) points[i].Y;
3240     }
3241
3242     save_state = SaveDC(graphics->hdc);
3243     EndPath(graphics->hdc);
3244     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
3245                                                                   : WINDING));
3246
3247     transform_and_round_points(graphics, pti, ptf, count);
3248
3249     BeginPath(graphics->hdc);
3250     Polygon(graphics->hdc, pti, count);
3251     EndPath(graphics->hdc);
3252
3253     brush_fill_path(graphics, brush);
3254
3255     RestoreDC(graphics->hdc, save_state);
3256
3257 end:
3258     GdipFree(ptf);
3259     GdipFree(pti);
3260
3261     return retval;
3262 }
3263
3264 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
3265     GDIPCONST GpPointF *points, INT count)
3266 {
3267     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3268
3269     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
3270 }
3271
3272 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
3273     GDIPCONST GpPoint *points, INT count)
3274 {
3275     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3276
3277     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
3278 }
3279
3280 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
3281     REAL x, REAL y, REAL width, REAL height)
3282 {
3283     INT save_state;
3284     GpPointF ptf[4];
3285     POINT pti[4];
3286
3287     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3288
3289     if(!graphics || !brush)
3290         return InvalidParameter;
3291
3292     if(graphics->busy)
3293         return ObjectBusy;
3294
3295     if(!graphics->hdc)
3296     {
3297         FIXME("graphics object has no HDC\n");
3298         return Ok;
3299     }
3300
3301     ptf[0].X = x;
3302     ptf[0].Y = y;
3303     ptf[1].X = x + width;
3304     ptf[1].Y = y;
3305     ptf[2].X = x + width;
3306     ptf[2].Y = y + height;
3307     ptf[3].X = x;
3308     ptf[3].Y = y + height;
3309
3310     save_state = SaveDC(graphics->hdc);
3311     EndPath(graphics->hdc);
3312
3313     transform_and_round_points(graphics, pti, ptf, 4);
3314
3315     BeginPath(graphics->hdc);
3316     Polygon(graphics->hdc, pti, 4);
3317     EndPath(graphics->hdc);
3318
3319     brush_fill_path(graphics, brush);
3320
3321     RestoreDC(graphics->hdc, save_state);
3322
3323     return Ok;
3324 }
3325
3326 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3327     INT x, INT y, INT width, INT height)
3328 {
3329     INT save_state;
3330     GpPointF ptf[4];
3331     POINT pti[4];
3332
3333     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3334
3335     if(!graphics || !brush)
3336         return InvalidParameter;
3337
3338     if(graphics->busy)
3339         return ObjectBusy;
3340
3341     if(!graphics->hdc)
3342     {
3343         FIXME("graphics object has no HDC\n");
3344         return Ok;
3345     }
3346
3347     ptf[0].X = x;
3348     ptf[0].Y = y;
3349     ptf[1].X = x + width;
3350     ptf[1].Y = y;
3351     ptf[2].X = x + width;
3352     ptf[2].Y = y + height;
3353     ptf[3].X = x;
3354     ptf[3].Y = y + height;
3355
3356     save_state = SaveDC(graphics->hdc);
3357     EndPath(graphics->hdc);
3358
3359     transform_and_round_points(graphics, pti, ptf, 4);
3360
3361     BeginPath(graphics->hdc);
3362     Polygon(graphics->hdc, pti, 4);
3363     EndPath(graphics->hdc);
3364
3365     brush_fill_path(graphics, brush);
3366
3367     RestoreDC(graphics->hdc, save_state);
3368
3369     return Ok;
3370 }
3371
3372 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3373     INT count)
3374 {
3375     GpStatus ret;
3376     INT i;
3377
3378     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3379
3380     if(!rects)
3381         return InvalidParameter;
3382
3383     for(i = 0; i < count; i++){
3384         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3385         if(ret != Ok)   return ret;
3386     }
3387
3388     return Ok;
3389 }
3390
3391 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3392     INT count)
3393 {
3394     GpRectF *rectsF;
3395     GpStatus ret;
3396     INT i;
3397
3398     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3399
3400     if(!rects || count <= 0)
3401         return InvalidParameter;
3402
3403     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3404     if(!rectsF)
3405         return OutOfMemory;
3406
3407     for(i = 0; i < count; i++){
3408         rectsF[i].X      = (REAL)rects[i].X;
3409         rectsF[i].Y      = (REAL)rects[i].Y;
3410         rectsF[i].X      = (REAL)rects[i].Width;
3411         rectsF[i].Height = (REAL)rects[i].Height;
3412     }
3413
3414     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3415     GdipFree(rectsF);
3416
3417     return ret;
3418 }
3419
3420 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3421     GpRegion* region)
3422 {
3423     INT save_state;
3424     GpStatus status;
3425     HRGN hrgn;
3426     RECT rc;
3427
3428     if(!graphics->hdc)
3429         return NotImplemented;
3430
3431     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3432     if(status != Ok)
3433         return status;
3434
3435     save_state = SaveDC(graphics->hdc);
3436     EndPath(graphics->hdc);
3437
3438     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3439
3440     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3441     {
3442         BeginPath(graphics->hdc);
3443         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3444         EndPath(graphics->hdc);
3445
3446         brush_fill_path(graphics, brush);
3447     }
3448
3449     RestoreDC(graphics->hdc, save_state);
3450
3451     DeleteObject(hrgn);
3452
3453     return Ok;
3454 }
3455
3456 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
3457     GpRegion* region)
3458 {
3459     GpStatus stat;
3460     GpRegion *temp_region;
3461     GpMatrix *world_to_device, *identity;
3462     GpRectF graphics_bounds;
3463     UINT scans_count, i;
3464     INT dummy;
3465     GpRect *scans;
3466     DWORD *pixel_data;
3467
3468     if (!brush_can_fill_pixels(brush))
3469         return NotImplemented;
3470
3471     stat = get_graphics_bounds(graphics, &graphics_bounds);
3472
3473     if (stat == Ok)
3474         stat = GdipCloneRegion(region, &temp_region);
3475
3476     if (stat == Ok)
3477     {
3478         stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
3479             CoordinateSpaceWorld, &world_to_device);
3480
3481         if (stat == Ok)
3482         {
3483             stat = GdipTransformRegion(temp_region, world_to_device);
3484
3485             GdipDeleteMatrix(world_to_device);
3486         }
3487
3488         if (stat == Ok)
3489             stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
3490
3491         if (stat == Ok)
3492             stat = GdipCreateMatrix(&identity);
3493
3494         if (stat == Ok)
3495         {
3496             stat = GdipGetRegionScansCount(temp_region, &scans_count, identity);
3497
3498             if (stat == Ok && scans_count != 0)
3499             {
3500                 scans = GdipAlloc(sizeof(*scans) * scans_count);
3501                 if (!scans)
3502                     stat = OutOfMemory;
3503
3504                 if (stat == Ok)
3505                 {
3506                     stat = GdipGetRegionScansI(temp_region, scans, &dummy, identity);
3507
3508                     if (stat != Ok)
3509                         GdipFree(scans);
3510                 }
3511             }
3512
3513             GdipDeleteMatrix(identity);
3514         }
3515
3516         GdipDeleteRegion(temp_region);
3517     }
3518
3519     if (stat == Ok && scans_count == 0)
3520         return Ok;
3521
3522     if (stat == Ok)
3523     {
3524         UINT max_size=0;
3525
3526         for (i=0; i<scans_count; i++)
3527         {
3528             UINT size = scans[i].Width * scans[i].Height;
3529
3530             if (size > max_size)
3531                 max_size = size;
3532         }
3533
3534         pixel_data = GdipAlloc(sizeof(*pixel_data) * max_size);
3535         if (!pixel_data)
3536             stat = OutOfMemory;
3537
3538         if (stat == Ok)
3539         {
3540             for (i=0; i<scans_count; i++)
3541             {
3542                 stat = brush_fill_pixels(graphics, brush, pixel_data, &scans[i],
3543                     scans[i].Width);
3544
3545                 if (stat == Ok)
3546                 {
3547                     stat = alpha_blend_pixels(graphics, scans[i].X, scans[i].Y,
3548                         (BYTE*)pixel_data, scans[i].Width, scans[i].Height,
3549                         scans[i].Width * 4);
3550                 }
3551
3552                 if (stat != Ok)
3553                     break;
3554             }
3555
3556             GdipFree(pixel_data);
3557         }
3558
3559         GdipFree(scans);
3560     }
3561
3562     return stat;
3563 }
3564
3565 /*****************************************************************************
3566  * GdipFillRegion [GDIPLUS.@]
3567  */
3568 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3569         GpRegion* region)
3570 {
3571     GpStatus stat = NotImplemented;
3572
3573     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3574
3575     if (!(graphics && brush && region))
3576         return InvalidParameter;
3577
3578     if(graphics->busy)
3579         return ObjectBusy;
3580
3581     if (!graphics->image)
3582         stat = GDI32_GdipFillRegion(graphics, brush, region);
3583
3584     if (stat == NotImplemented)
3585         stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
3586
3587     if (stat == NotImplemented && graphics->image)
3588         stat = GDI32_GdipFillRegion(graphics, brush, region);
3589
3590     if (stat == NotImplemented)
3591     {
3592         FIXME("not implemented for brushtype %i\n", brush->bt);
3593         stat = Ok;
3594     }
3595
3596     return stat;
3597 }
3598
3599 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3600 {
3601     TRACE("(%p,%u)\n", graphics, intention);
3602
3603     if(!graphics)
3604         return InvalidParameter;
3605
3606     if(graphics->busy)
3607         return ObjectBusy;
3608
3609     /* We have no internal operation queue, so there's no need to clear it. */
3610
3611     if (graphics->hdc)
3612         GdiFlush();
3613
3614     return Ok;
3615 }
3616
3617 /*****************************************************************************
3618  * GdipGetClipBounds [GDIPLUS.@]
3619  */
3620 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3621 {
3622     TRACE("(%p, %p)\n", graphics, rect);
3623
3624     if(!graphics)
3625         return InvalidParameter;
3626
3627     if(graphics->busy)
3628         return ObjectBusy;
3629
3630     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3631 }
3632
3633 /*****************************************************************************
3634  * GdipGetClipBoundsI [GDIPLUS.@]
3635  */
3636 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3637 {
3638     TRACE("(%p, %p)\n", graphics, rect);
3639
3640     if(!graphics)
3641         return InvalidParameter;
3642
3643     if(graphics->busy)
3644         return ObjectBusy;
3645
3646     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3647 }
3648
3649 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3650 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3651     CompositingMode *mode)
3652 {
3653     TRACE("(%p, %p)\n", graphics, mode);
3654
3655     if(!graphics || !mode)
3656         return InvalidParameter;
3657
3658     if(graphics->busy)
3659         return ObjectBusy;
3660
3661     *mode = graphics->compmode;
3662
3663     return Ok;
3664 }
3665
3666 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3667 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3668     CompositingQuality *quality)
3669 {
3670     TRACE("(%p, %p)\n", graphics, quality);
3671
3672     if(!graphics || !quality)
3673         return InvalidParameter;
3674
3675     if(graphics->busy)
3676         return ObjectBusy;
3677
3678     *quality = graphics->compqual;
3679
3680     return Ok;
3681 }
3682
3683 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
3684 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
3685     InterpolationMode *mode)
3686 {
3687     TRACE("(%p, %p)\n", graphics, mode);
3688
3689     if(!graphics || !mode)
3690         return InvalidParameter;
3691
3692     if(graphics->busy)
3693         return ObjectBusy;
3694
3695     *mode = graphics->interpolation;
3696
3697     return Ok;
3698 }
3699
3700 /* FIXME: Need to handle color depths less than 24bpp */
3701 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
3702 {
3703     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
3704
3705     if(!graphics || !argb)
3706         return InvalidParameter;
3707
3708     if(graphics->busy)
3709         return ObjectBusy;
3710
3711     return Ok;
3712 }
3713
3714 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
3715 {
3716     TRACE("(%p, %p)\n", graphics, scale);
3717
3718     if(!graphics || !scale)
3719         return InvalidParameter;
3720
3721     if(graphics->busy)
3722         return ObjectBusy;
3723
3724     *scale = graphics->scale;
3725
3726     return Ok;
3727 }
3728
3729 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
3730 {
3731     TRACE("(%p, %p)\n", graphics, unit);
3732
3733     if(!graphics || !unit)
3734         return InvalidParameter;
3735
3736     if(graphics->busy)
3737         return ObjectBusy;
3738
3739     *unit = graphics->unit;
3740
3741     return Ok;
3742 }
3743
3744 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
3745 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3746     *mode)
3747 {
3748     TRACE("(%p, %p)\n", graphics, mode);
3749
3750     if(!graphics || !mode)
3751         return InvalidParameter;
3752
3753     if(graphics->busy)
3754         return ObjectBusy;
3755
3756     *mode = graphics->pixeloffset;
3757
3758     return Ok;
3759 }
3760
3761 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
3762 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
3763 {
3764     TRACE("(%p, %p)\n", graphics, mode);
3765
3766     if(!graphics || !mode)
3767         return InvalidParameter;
3768
3769     if(graphics->busy)
3770         return ObjectBusy;
3771
3772     *mode = graphics->smoothing;
3773
3774     return Ok;
3775 }
3776
3777 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
3778 {
3779     TRACE("(%p, %p)\n", graphics, contrast);
3780
3781     if(!graphics || !contrast)
3782         return InvalidParameter;
3783
3784     *contrast = graphics->textcontrast;
3785
3786     return Ok;
3787 }
3788
3789 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
3790 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
3791     TextRenderingHint *hint)
3792 {
3793     TRACE("(%p, %p)\n", graphics, hint);
3794
3795     if(!graphics || !hint)
3796         return InvalidParameter;
3797
3798     if(graphics->busy)
3799         return ObjectBusy;
3800
3801     *hint = graphics->texthint;
3802
3803     return Ok;
3804 }
3805
3806 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
3807 {
3808     GpRegion *clip_rgn;
3809     GpStatus stat;
3810
3811     TRACE("(%p, %p)\n", graphics, rect);
3812
3813     if(!graphics || !rect)
3814         return InvalidParameter;
3815
3816     if(graphics->busy)
3817         return ObjectBusy;
3818
3819     /* intersect window and graphics clipping regions */
3820     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
3821         return stat;
3822
3823     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
3824         goto cleanup;
3825
3826     /* get bounds of the region */
3827     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
3828
3829 cleanup:
3830     GdipDeleteRegion(clip_rgn);
3831
3832     return stat;
3833 }
3834
3835 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3836 {
3837     GpRectF rectf;
3838     GpStatus stat;
3839
3840     TRACE("(%p, %p)\n", graphics, rect);
3841
3842     if(!graphics || !rect)
3843         return InvalidParameter;
3844
3845     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
3846     {
3847         rect->X = roundr(rectf.X);
3848         rect->Y = roundr(rectf.Y);
3849         rect->Width  = roundr(rectf.Width);
3850         rect->Height = roundr(rectf.Height);
3851     }
3852
3853     return stat;
3854 }
3855
3856 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3857 {
3858     TRACE("(%p, %p)\n", graphics, matrix);
3859
3860     if(!graphics || !matrix)
3861         return InvalidParameter;
3862
3863     if(graphics->busy)
3864         return ObjectBusy;
3865
3866     *matrix = *graphics->worldtrans;
3867     return Ok;
3868 }
3869
3870 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
3871 {
3872     GpSolidFill *brush;
3873     GpStatus stat;
3874     GpRectF wnd_rect;
3875
3876     TRACE("(%p, %x)\n", graphics, color);
3877
3878     if(!graphics)
3879         return InvalidParameter;
3880
3881     if(graphics->busy)
3882         return ObjectBusy;
3883
3884     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3885         return stat;
3886
3887     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
3888         GdipDeleteBrush((GpBrush*)brush);
3889         return stat;
3890     }
3891
3892     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
3893                                                  wnd_rect.Width, wnd_rect.Height);
3894
3895     GdipDeleteBrush((GpBrush*)brush);
3896
3897     return Ok;
3898 }
3899
3900 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3901 {
3902     TRACE("(%p, %p)\n", graphics, res);
3903
3904     if(!graphics || !res)
3905         return InvalidParameter;
3906
3907     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3908 }
3909
3910 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3911 {
3912     GpStatus stat;
3913     GpRegion* rgn;
3914     GpPointF pt;
3915
3916     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
3917
3918     if(!graphics || !result)
3919         return InvalidParameter;
3920
3921     if(graphics->busy)
3922         return ObjectBusy;
3923
3924     pt.X = x;
3925     pt.Y = y;
3926     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3927                    CoordinateSpaceWorld, &pt, 1)) != Ok)
3928         return stat;
3929
3930     if((stat = GdipCreateRegion(&rgn)) != Ok)
3931         return stat;
3932
3933     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3934         goto cleanup;
3935
3936     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
3937
3938 cleanup:
3939     GdipDeleteRegion(rgn);
3940     return stat;
3941 }
3942
3943 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3944 {
3945     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
3946 }
3947
3948 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
3949 {
3950     GpStatus stat;
3951     GpRegion* rgn;
3952     GpPointF pts[2];
3953
3954     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
3955
3956     if(!graphics || !result)
3957         return InvalidParameter;
3958
3959     if(graphics->busy)
3960         return ObjectBusy;
3961
3962     pts[0].X = x;
3963     pts[0].Y = y;
3964     pts[1].X = x + width;
3965     pts[1].Y = y + height;
3966
3967     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3968                     CoordinateSpaceWorld, pts, 2)) != Ok)
3969         return stat;
3970
3971     pts[1].X -= pts[0].X;
3972     pts[1].Y -= pts[0].Y;
3973
3974     if((stat = GdipCreateRegion(&rgn)) != Ok)
3975         return stat;
3976
3977     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3978         goto cleanup;
3979
3980     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
3981
3982 cleanup:
3983     GdipDeleteRegion(rgn);
3984     return stat;
3985 }
3986
3987 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
3988 {
3989     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
3990 }
3991
3992 GpStatus gdip_format_string(HDC hdc,
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(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(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(hdc, 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(HDC hdc,
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(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(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 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         hdc = temp_hdc = CreateCompatibleDC(0);
4183         if (!temp_hdc) return OutOfMemory;
4184     }
4185     else
4186         hdc = graphics->hdc;
4187
4188     if (stringFormat->attr)
4189         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
4190
4191     oldfont = SelectObject(hdc, CreateFontIndirectW(&font->lfw));
4192
4193     for (i=0; i<stringFormat->range_count; i++)
4194     {
4195         stat = GdipSetEmpty(regions[i]);
4196         if (stat != Ok)
4197             return stat;
4198     }
4199
4200     args.regions = regions;
4201
4202     stat = gdip_format_string(hdc, string, length, font, layoutRect, stringFormat,
4203         measure_ranges_callback, &args);
4204
4205     DeleteObject(SelectObject(hdc, oldfont));
4206
4207     if (temp_hdc)
4208         DeleteDC(temp_hdc);
4209
4210     return stat;
4211 }
4212
4213 struct measure_string_args {
4214     RectF *bounds;
4215     INT *codepointsfitted;
4216     INT *linesfilled;
4217 };
4218
4219 static GpStatus measure_string_callback(HDC hdc,
4220     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4221     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4222     INT lineno, const RectF *bounds, void *user_data)
4223 {
4224     struct measure_string_args *args = user_data;
4225
4226     if (bounds->Width > args->bounds->Width)
4227         args->bounds->Width = bounds->Width;
4228
4229     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
4230         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
4231
4232     if (args->codepointsfitted)
4233         *args->codepointsfitted = index + length;
4234
4235     if (args->linesfilled)
4236         (*args->linesfilled)++;
4237
4238     return Ok;
4239 }
4240
4241 /* Find the smallest rectangle that bounds the text when it is printed in rect
4242  * according to the format options listed in format. If rect has 0 width and
4243  * height, then just find the smallest rectangle that bounds the text when it's
4244  * printed at location (rect->X, rect-Y). */
4245 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
4246     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4247     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
4248     INT *codepointsfitted, INT *linesfilled)
4249 {
4250     HFONT oldfont;
4251     struct measure_string_args args;
4252     HDC temp_hdc=NULL;
4253
4254     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
4255         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
4256         bounds, codepointsfitted, linesfilled);
4257
4258     if(!graphics || !string || !font || !rect || !bounds)
4259         return InvalidParameter;
4260
4261     if(!graphics->hdc)
4262     {
4263         temp_hdc = CreateCompatibleDC(0);
4264         if (!temp_hdc) return OutOfMemory;
4265     }
4266
4267     if(linesfilled) *linesfilled = 0;
4268     if(codepointsfitted) *codepointsfitted = 0;
4269
4270     if(format)
4271         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4272
4273     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
4274
4275     bounds->X = rect->X;
4276     bounds->Y = rect->Y;
4277     bounds->Width = 0.0;
4278     bounds->Height = 0.0;
4279
4280     args.bounds = bounds;
4281     args.codepointsfitted = codepointsfitted;
4282     args.linesfilled = linesfilled;
4283
4284     gdip_format_string(graphics->hdc ? graphics->hdc : temp_hdc, string, length, font, rect, format,
4285         measure_string_callback, &args);
4286
4287     DeleteObject(SelectObject(graphics->hdc, oldfont));
4288
4289     if (temp_hdc)
4290         DeleteDC(temp_hdc);
4291
4292     return Ok;
4293 }
4294
4295 struct draw_string_args {
4296     POINT drawbase;
4297     UINT drawflags;
4298     REAL ang_cos, ang_sin;
4299 };
4300
4301 static GpStatus draw_string_callback(HDC hdc,
4302     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4303     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4304     INT lineno, const RectF *bounds, void *user_data)
4305 {
4306     struct draw_string_args *args = user_data;
4307     RECT drawcoord;
4308
4309     drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
4310     drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
4311
4312     DrawTextW(hdc, string + index, length, &drawcoord, args->drawflags);
4313
4314     return Ok;
4315 }
4316
4317 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
4318     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
4319     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
4320 {
4321     HRGN rgn = NULL;
4322     HFONT gdifont;
4323     LOGFONTW lfw;
4324     TEXTMETRICW textmet;
4325     GpPointF pt[3], rectcpy[4];
4326     POINT corners[4];
4327     REAL angle, rel_width, rel_height;
4328     INT offsety = 0, save_state;
4329     struct draw_string_args args;
4330     RectF scaled_rect;
4331
4332     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
4333         length, font, debugstr_rectf(rect), format, brush);
4334
4335     if(!graphics || !string || !font || !brush || !rect)
4336         return InvalidParameter;
4337
4338     if((brush->bt != BrushTypeSolidColor)){
4339         FIXME("not implemented for given parameters\n");
4340         return NotImplemented;
4341     }
4342
4343     if(!graphics->hdc)
4344     {
4345         FIXME("graphics object has no HDC\n");
4346         return Ok;
4347     }
4348
4349     if(format){
4350         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4351
4352         /* Should be no need to explicitly test for StringAlignmentNear as
4353          * that is default behavior if no alignment is passed. */
4354         if(format->vertalign != StringAlignmentNear){
4355             RectF bounds;
4356             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
4357
4358             if(format->vertalign == StringAlignmentCenter)
4359                 offsety = (rect->Height - bounds.Height) / 2;
4360             else if(format->vertalign == StringAlignmentFar)
4361                 offsety = (rect->Height - bounds.Height);
4362         }
4363     }
4364
4365     save_state = SaveDC(graphics->hdc);
4366     SetBkMode(graphics->hdc, TRANSPARENT);
4367     SetTextColor(graphics->hdc, brush->lb.lbColor);
4368
4369     pt[0].X = 0.0;
4370     pt[0].Y = 0.0;
4371     pt[1].X = 1.0;
4372     pt[1].Y = 0.0;
4373     pt[2].X = 0.0;
4374     pt[2].Y = 1.0;
4375     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4376     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
4377     args.ang_cos = cos(angle);
4378     args.ang_sin = sin(angle);
4379     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4380                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4381     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4382                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4383
4384     rectcpy[3].X = rectcpy[0].X = rect->X;
4385     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
4386     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
4387     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
4388     transform_and_round_points(graphics, corners, rectcpy, 4);
4389
4390     scaled_rect.X = 0.0;
4391     scaled_rect.Y = 0.0;
4392     scaled_rect.Width = rel_width * rect->Width;
4393     scaled_rect.Height = rel_height * rect->Height;
4394
4395     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
4396     {
4397         /* FIXME: If only the width or only the height is 0, we should probably still clip */
4398         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
4399         SelectClipRgn(graphics->hdc, rgn);
4400     }
4401
4402     /* Use gdi to find the font, then perform transformations on it (height,
4403      * width, angle). */
4404     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
4405     GetTextMetricsW(graphics->hdc, &textmet);
4406     lfw = font->lfw;
4407
4408     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
4409     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
4410
4411     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
4412
4413     gdifont = CreateFontIndirectW(&lfw);
4414     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
4415
4416     if (!format || format->align == StringAlignmentNear)
4417     {
4418         args.drawbase.x = corners[0].x;
4419         args.drawbase.y = corners[0].y;
4420         args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
4421     }
4422     else if (format->align == StringAlignmentCenter)
4423     {
4424         args.drawbase.x = (corners[0].x + corners[1].x)/2;
4425         args.drawbase.y = (corners[0].y + corners[1].y)/2;
4426         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
4427     }
4428     else /* (format->align == StringAlignmentFar) */
4429     {
4430         args.drawbase.x = corners[1].x;
4431         args.drawbase.y = corners[1].y;
4432         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
4433     }
4434
4435     gdip_format_string(graphics->hdc, string, length, font, &scaled_rect, format,
4436         draw_string_callback, &args);
4437
4438     DeleteObject(rgn);
4439     DeleteObject(gdifont);
4440
4441     RestoreDC(graphics->hdc, save_state);
4442
4443     return Ok;
4444 }
4445
4446 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
4447 {
4448     TRACE("(%p)\n", graphics);
4449
4450     if(!graphics)
4451         return InvalidParameter;
4452
4453     if(graphics->busy)
4454         return ObjectBusy;
4455
4456     return GdipSetInfinite(graphics->clip);
4457 }
4458
4459 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
4460 {
4461     TRACE("(%p)\n", graphics);
4462
4463     if(!graphics)
4464         return InvalidParameter;
4465
4466     if(graphics->busy)
4467         return ObjectBusy;
4468
4469     graphics->worldtrans->matrix[0] = 1.0;
4470     graphics->worldtrans->matrix[1] = 0.0;
4471     graphics->worldtrans->matrix[2] = 0.0;
4472     graphics->worldtrans->matrix[3] = 1.0;
4473     graphics->worldtrans->matrix[4] = 0.0;
4474     graphics->worldtrans->matrix[5] = 0.0;
4475
4476     return Ok;
4477 }
4478
4479 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
4480 {
4481     return GdipEndContainer(graphics, state);
4482 }
4483
4484 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
4485     GpMatrixOrder order)
4486 {
4487     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
4488
4489     if(!graphics)
4490         return InvalidParameter;
4491
4492     if(graphics->busy)
4493         return ObjectBusy;
4494
4495     return GdipRotateMatrix(graphics->worldtrans, angle, order);
4496 }
4497
4498 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
4499 {
4500     return GdipBeginContainer2(graphics, state);
4501 }
4502
4503 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
4504         GraphicsContainer *state)
4505 {
4506     GraphicsContainerItem *container;
4507     GpStatus sts;
4508
4509     TRACE("(%p, %p)\n", graphics, state);
4510
4511     if(!graphics || !state)
4512         return InvalidParameter;
4513
4514     sts = init_container(&container, graphics);
4515     if(sts != Ok)
4516         return sts;
4517
4518     list_add_head(&graphics->containers, &container->entry);
4519     *state = graphics->contid = container->contid;
4520
4521     return Ok;
4522 }
4523
4524 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
4525 {
4526     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4527     return NotImplemented;
4528 }
4529
4530 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
4531 {
4532     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4533     return NotImplemented;
4534 }
4535
4536 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4537 {
4538     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4539     return NotImplemented;
4540 }
4541
4542 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4543 {
4544     GpStatus sts;
4545     GraphicsContainerItem *container, *container2;
4546
4547     TRACE("(%p, %x)\n", graphics, state);
4548
4549     if(!graphics)
4550         return InvalidParameter;
4551
4552     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4553         if(container->contid == state)
4554             break;
4555     }
4556
4557     /* did not find a matching container */
4558     if(&container->entry == &graphics->containers)
4559         return Ok;
4560
4561     sts = restore_container(graphics, container);
4562     if(sts != Ok)
4563         return sts;
4564
4565     /* remove all of the containers on top of the found container */
4566     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4567         if(container->contid == state)
4568             break;
4569         list_remove(&container->entry);
4570         delete_container(container);
4571     }
4572
4573     list_remove(&container->entry);
4574     delete_container(container);
4575
4576     return Ok;
4577 }
4578
4579 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4580     REAL sy, GpMatrixOrder order)
4581 {
4582     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4583
4584     if(!graphics)
4585         return InvalidParameter;
4586
4587     if(graphics->busy)
4588         return ObjectBusy;
4589
4590     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4591 }
4592
4593 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4594     CombineMode mode)
4595 {
4596     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4597
4598     if(!graphics || !srcgraphics)
4599         return InvalidParameter;
4600
4601     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4602 }
4603
4604 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4605     CompositingMode mode)
4606 {
4607     TRACE("(%p, %d)\n", graphics, mode);
4608
4609     if(!graphics)
4610         return InvalidParameter;
4611
4612     if(graphics->busy)
4613         return ObjectBusy;
4614
4615     graphics->compmode = mode;
4616
4617     return Ok;
4618 }
4619
4620 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4621     CompositingQuality quality)
4622 {
4623     TRACE("(%p, %d)\n", graphics, quality);
4624
4625     if(!graphics)
4626         return InvalidParameter;
4627
4628     if(graphics->busy)
4629         return ObjectBusy;
4630
4631     graphics->compqual = quality;
4632
4633     return Ok;
4634 }
4635
4636 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4637     InterpolationMode mode)
4638 {
4639     TRACE("(%p, %d)\n", graphics, mode);
4640
4641     if(!graphics)
4642         return InvalidParameter;
4643
4644     if(graphics->busy)
4645         return ObjectBusy;
4646
4647     graphics->interpolation = mode;
4648
4649     return Ok;
4650 }
4651
4652 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4653 {
4654     TRACE("(%p, %.2f)\n", graphics, scale);
4655
4656     if(!graphics || (scale <= 0.0))
4657         return InvalidParameter;
4658
4659     if(graphics->busy)
4660         return ObjectBusy;
4661
4662     graphics->scale = scale;
4663
4664     return Ok;
4665 }
4666
4667 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4668 {
4669     TRACE("(%p, %d)\n", graphics, unit);
4670
4671     if(!graphics)
4672         return InvalidParameter;
4673
4674     if(graphics->busy)
4675         return ObjectBusy;
4676
4677     if(unit == UnitWorld)
4678         return InvalidParameter;
4679
4680     graphics->unit = unit;
4681
4682     return Ok;
4683 }
4684
4685 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4686     mode)
4687 {
4688     TRACE("(%p, %d)\n", graphics, mode);
4689
4690     if(!graphics)
4691         return InvalidParameter;
4692
4693     if(graphics->busy)
4694         return ObjectBusy;
4695
4696     graphics->pixeloffset = mode;
4697
4698     return Ok;
4699 }
4700
4701 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
4702 {
4703     static int calls;
4704
4705     TRACE("(%p,%i,%i)\n", graphics, x, y);
4706
4707     if (!(calls++))
4708         FIXME("not implemented\n");
4709
4710     return NotImplemented;
4711 }
4712
4713 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
4714 {
4715     static int calls;
4716
4717     TRACE("(%p,%p,%p)\n", graphics, x, y);
4718
4719     if (!(calls++))
4720         FIXME("not implemented\n");
4721
4722     *x = *y = 0;
4723
4724     return NotImplemented;
4725 }
4726
4727 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
4728 {
4729     TRACE("(%p, %d)\n", graphics, mode);
4730
4731     if(!graphics)
4732         return InvalidParameter;
4733
4734     if(graphics->busy)
4735         return ObjectBusy;
4736
4737     graphics->smoothing = mode;
4738
4739     return Ok;
4740 }
4741
4742 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
4743 {
4744     TRACE("(%p, %d)\n", graphics, contrast);
4745
4746     if(!graphics)
4747         return InvalidParameter;
4748
4749     graphics->textcontrast = contrast;
4750
4751     return Ok;
4752 }
4753
4754 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
4755     TextRenderingHint hint)
4756 {
4757     TRACE("(%p, %d)\n", graphics, hint);
4758
4759     if(!graphics)
4760         return InvalidParameter;
4761
4762     if(graphics->busy)
4763         return ObjectBusy;
4764
4765     graphics->texthint = hint;
4766
4767     return Ok;
4768 }
4769
4770 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4771 {
4772     TRACE("(%p, %p)\n", graphics, matrix);
4773
4774     if(!graphics || !matrix)
4775         return InvalidParameter;
4776
4777     if(graphics->busy)
4778         return ObjectBusy;
4779
4780     GdipDeleteMatrix(graphics->worldtrans);
4781     return GdipCloneMatrix(matrix, &graphics->worldtrans);
4782 }
4783
4784 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
4785     REAL dy, GpMatrixOrder order)
4786 {
4787     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
4788
4789     if(!graphics)
4790         return InvalidParameter;
4791
4792     if(graphics->busy)
4793         return ObjectBusy;
4794
4795     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
4796 }
4797
4798 /*****************************************************************************
4799  * GdipSetClipHrgn [GDIPLUS.@]
4800  */
4801 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
4802 {
4803     GpRegion *region;
4804     GpStatus status;
4805
4806     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
4807
4808     if(!graphics)
4809         return InvalidParameter;
4810
4811     status = GdipCreateRegionHrgn(hrgn, &region);
4812     if(status != Ok)
4813         return status;
4814
4815     status = GdipSetClipRegion(graphics, region, mode);
4816
4817     GdipDeleteRegion(region);
4818     return status;
4819 }
4820
4821 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
4822 {
4823     TRACE("(%p, %p, %d)\n", graphics, path, mode);
4824
4825     if(!graphics)
4826         return InvalidParameter;
4827
4828     if(graphics->busy)
4829         return ObjectBusy;
4830
4831     return GdipCombineRegionPath(graphics->clip, path, mode);
4832 }
4833
4834 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
4835                                     REAL width, REAL height,
4836                                     CombineMode mode)
4837 {
4838     GpRectF rect;
4839
4840     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
4841
4842     if(!graphics)
4843         return InvalidParameter;
4844
4845     if(graphics->busy)
4846         return ObjectBusy;
4847
4848     rect.X = x;
4849     rect.Y = y;
4850     rect.Width  = width;
4851     rect.Height = height;
4852
4853     return GdipCombineRegionRect(graphics->clip, &rect, mode);
4854 }
4855
4856 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
4857                                      INT width, INT height,
4858                                      CombineMode mode)
4859 {
4860     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
4861
4862     if(!graphics)
4863         return InvalidParameter;
4864
4865     if(graphics->busy)
4866         return ObjectBusy;
4867
4868     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
4869 }
4870
4871 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
4872                                       CombineMode mode)
4873 {
4874     TRACE("(%p, %p, %d)\n", graphics, region, mode);
4875
4876     if(!graphics || !region)
4877         return InvalidParameter;
4878
4879     if(graphics->busy)
4880         return ObjectBusy;
4881
4882     return GdipCombineRegionRegion(graphics->clip, region, mode);
4883 }
4884
4885 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4886     UINT limitDpi)
4887 {
4888     static int calls;
4889
4890     TRACE("(%p,%u)\n", metafile, limitDpi);
4891
4892     if(!(calls++))
4893         FIXME("not implemented\n");
4894
4895     return NotImplemented;
4896 }
4897
4898 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
4899     INT count)
4900 {
4901     INT save_state;
4902     POINT *pti;
4903
4904     TRACE("(%p, %p, %d)\n", graphics, points, count);
4905
4906     if(!graphics || !pen || count<=0)
4907         return InvalidParameter;
4908
4909     if(graphics->busy)
4910         return ObjectBusy;
4911
4912     if (!graphics->hdc)
4913     {
4914         FIXME("graphics object has no HDC\n");
4915         return Ok;
4916     }
4917
4918     pti = GdipAlloc(sizeof(POINT) * count);
4919
4920     save_state = prepare_dc(graphics, pen);
4921     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
4922
4923     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
4924     Polygon(graphics->hdc, pti, count);
4925
4926     restore_dc(graphics, save_state);
4927     GdipFree(pti);
4928
4929     return Ok;
4930 }
4931
4932 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
4933     INT count)
4934 {
4935     GpStatus ret;
4936     GpPointF *ptf;
4937     INT i;
4938
4939     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
4940
4941     if(count<=0)    return InvalidParameter;
4942     ptf = GdipAlloc(sizeof(GpPointF) * count);
4943
4944     for(i = 0;i < count; i++){
4945         ptf[i].X = (REAL)points[i].X;
4946         ptf[i].Y = (REAL)points[i].Y;
4947     }
4948
4949     ret = GdipDrawPolygon(graphics,pen,ptf,count);
4950     GdipFree(ptf);
4951
4952     return ret;
4953 }
4954
4955 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
4956 {
4957     TRACE("(%p, %p)\n", graphics, dpi);
4958
4959     if(!graphics || !dpi)
4960         return InvalidParameter;
4961
4962     if(graphics->busy)
4963         return ObjectBusy;
4964
4965     if (graphics->image)
4966         *dpi = graphics->image->xres;
4967     else
4968         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
4969
4970     return Ok;
4971 }
4972
4973 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
4974 {
4975     TRACE("(%p, %p)\n", graphics, dpi);
4976
4977     if(!graphics || !dpi)
4978         return InvalidParameter;
4979
4980     if(graphics->busy)
4981         return ObjectBusy;
4982
4983     if (graphics->image)
4984         *dpi = graphics->image->yres;
4985     else
4986         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
4987
4988     return Ok;
4989 }
4990
4991 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
4992     GpMatrixOrder order)
4993 {
4994     GpMatrix m;
4995     GpStatus ret;
4996
4997     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
4998
4999     if(!graphics || !matrix)
5000         return InvalidParameter;
5001
5002     if(graphics->busy)
5003         return ObjectBusy;
5004
5005     m = *(graphics->worldtrans);
5006
5007     ret = GdipMultiplyMatrix(&m, matrix, order);
5008     if(ret == Ok)
5009         *(graphics->worldtrans) = m;
5010
5011     return ret;
5012 }
5013
5014 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
5015 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
5016
5017 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
5018 {
5019     TRACE("(%p, %p)\n", graphics, hdc);
5020
5021     if(!graphics || !hdc)
5022         return InvalidParameter;
5023
5024     if(graphics->busy)
5025         return ObjectBusy;
5026
5027     if (!graphics->hdc ||
5028         (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
5029     {
5030         /* Create a fake HDC and fill it with a constant color. */
5031         HDC temp_hdc;
5032         HBITMAP hbitmap;
5033         GpStatus stat;
5034         GpRectF bounds;
5035         BITMAPINFOHEADER bmih;
5036         int i;
5037
5038         stat = get_graphics_bounds(graphics, &bounds);
5039         if (stat != Ok)
5040             return stat;
5041
5042         graphics->temp_hbitmap_width = bounds.Width;
5043         graphics->temp_hbitmap_height = bounds.Height;
5044
5045         bmih.biSize = sizeof(bmih);
5046         bmih.biWidth = graphics->temp_hbitmap_width;
5047         bmih.biHeight = -graphics->temp_hbitmap_height;
5048         bmih.biPlanes = 1;
5049         bmih.biBitCount = 32;
5050         bmih.biCompression = BI_RGB;
5051         bmih.biSizeImage = 0;
5052         bmih.biXPelsPerMeter = 0;
5053         bmih.biYPelsPerMeter = 0;
5054         bmih.biClrUsed = 0;
5055         bmih.biClrImportant = 0;
5056
5057         hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
5058             (void**)&graphics->temp_bits, NULL, 0);
5059         if (!hbitmap)
5060             return GenericError;
5061
5062         temp_hdc = CreateCompatibleDC(0);
5063         if (!temp_hdc)
5064         {
5065             DeleteObject(hbitmap);
5066             return GenericError;
5067         }
5068
5069         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5070             ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
5071
5072         SelectObject(temp_hdc, hbitmap);
5073
5074         graphics->temp_hbitmap = hbitmap;
5075         *hdc = graphics->temp_hdc = temp_hdc;
5076     }
5077     else
5078     {
5079         *hdc = graphics->hdc;
5080     }
5081
5082     graphics->busy = TRUE;
5083
5084     return Ok;
5085 }
5086
5087 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
5088 {
5089     TRACE("(%p, %p)\n", graphics, hdc);
5090
5091     if(!graphics || !hdc)
5092         return InvalidParameter;
5093
5094     if((graphics->hdc != hdc && graphics->temp_hdc != hdc) || !(graphics->busy))
5095         return InvalidParameter;
5096
5097     if (graphics->temp_hdc == hdc)
5098     {
5099         DWORD* pos;
5100         int i;
5101
5102         /* Find the pixels that have changed, and mark them as opaque. */
5103         pos = (DWORD*)graphics->temp_bits;
5104         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5105         {
5106             if (*pos != DC_BACKGROUND_KEY)
5107             {
5108                 *pos |= 0xff000000;
5109             }
5110             pos++;
5111         }
5112
5113         /* Write the changed pixels to the real target. */
5114         alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
5115             graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
5116             graphics->temp_hbitmap_width * 4);
5117
5118         /* Clean up. */
5119         DeleteDC(graphics->temp_hdc);
5120         DeleteObject(graphics->temp_hbitmap);
5121         graphics->temp_hdc = NULL;
5122         graphics->temp_hbitmap = NULL;
5123     }
5124
5125     graphics->busy = FALSE;
5126
5127     return Ok;
5128 }
5129
5130 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
5131 {
5132     GpRegion *clip;
5133     GpStatus status;
5134
5135     TRACE("(%p, %p)\n", graphics, region);
5136
5137     if(!graphics || !region)
5138         return InvalidParameter;
5139
5140     if(graphics->busy)
5141         return ObjectBusy;
5142
5143     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
5144         return status;
5145
5146     /* free everything except root node and header */
5147     delete_element(&region->node);
5148     memcpy(region, clip, sizeof(GpRegion));
5149     GdipFree(clip);
5150
5151     return Ok;
5152 }
5153
5154 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
5155         GpCoordinateSpace src_space, GpMatrix **matrix)
5156 {
5157     GpStatus stat = GdipCreateMatrix(matrix);
5158     REAL unitscale;
5159
5160     if (dst_space != src_space && stat == Ok)
5161     {
5162         unitscale = convert_unit(graphics_res(graphics), graphics->unit);
5163
5164         if(graphics->unit != UnitDisplay)
5165             unitscale *= graphics->scale;
5166
5167         /* transform from src_space to CoordinateSpacePage */
5168         switch (src_space)
5169         {
5170         case CoordinateSpaceWorld:
5171             GdipMultiplyMatrix(*matrix, graphics->worldtrans, MatrixOrderAppend);
5172             break;
5173         case CoordinateSpacePage:
5174             break;
5175         case CoordinateSpaceDevice:
5176             GdipScaleMatrix(*matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
5177             break;
5178         }
5179
5180         /* transform from CoordinateSpacePage to dst_space */
5181         switch (dst_space)
5182         {
5183         case CoordinateSpaceWorld:
5184             {
5185                 GpMatrix *inverted_transform;
5186                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
5187                 if (stat == Ok)
5188                 {
5189                     stat = GdipInvertMatrix(inverted_transform);
5190                     if (stat == Ok)
5191                         GdipMultiplyMatrix(*matrix, inverted_transform, MatrixOrderAppend);
5192                     GdipDeleteMatrix(inverted_transform);
5193                 }
5194                 break;
5195             }
5196         case CoordinateSpacePage:
5197             break;
5198         case CoordinateSpaceDevice:
5199             GdipScaleMatrix(*matrix, unitscale, unitscale, MatrixOrderAppend);
5200             break;
5201         }
5202     }
5203     return stat;
5204 }
5205
5206 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
5207                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
5208 {
5209     GpMatrix *matrix;
5210     GpStatus stat;
5211
5212     if(!graphics || !points || count <= 0)
5213         return InvalidParameter;
5214
5215     if(graphics->busy)
5216         return ObjectBusy;
5217
5218     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5219
5220     if (src_space == dst_space) return Ok;
5221
5222     stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
5223
5224     if (stat == Ok)
5225     {
5226         stat = GdipTransformMatrixPoints(matrix, points, count);
5227
5228         GdipDeleteMatrix(matrix);
5229     }
5230
5231     return stat;
5232 }
5233
5234 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
5235                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
5236 {
5237     GpPointF *pointsF;
5238     GpStatus ret;
5239     INT i;
5240
5241     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5242
5243     if(count <= 0)
5244         return InvalidParameter;
5245
5246     pointsF = GdipAlloc(sizeof(GpPointF) * count);
5247     if(!pointsF)
5248         return OutOfMemory;
5249
5250     for(i = 0; i < count; i++){
5251         pointsF[i].X = (REAL)points[i].X;
5252         pointsF[i].Y = (REAL)points[i].Y;
5253     }
5254
5255     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
5256
5257     if(ret == Ok)
5258         for(i = 0; i < count; i++){
5259             points[i].X = roundr(pointsF[i].X);
5260             points[i].Y = roundr(pointsF[i].Y);
5261         }
5262     GdipFree(pointsF);
5263
5264     return ret;
5265 }
5266
5267 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
5268 {
5269     static int calls;
5270
5271     TRACE("\n");
5272
5273     if (!calls++)
5274       FIXME("stub\n");
5275
5276     return NULL;
5277 }
5278
5279 /*****************************************************************************
5280  * GdipTranslateClip [GDIPLUS.@]
5281  */
5282 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
5283 {
5284     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
5285
5286     if(!graphics)
5287         return InvalidParameter;
5288
5289     if(graphics->busy)
5290         return ObjectBusy;
5291
5292     return GdipTranslateRegion(graphics->clip, dx, dy);
5293 }
5294
5295 /*****************************************************************************
5296  * GdipTranslateClipI [GDIPLUS.@]
5297  */
5298 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
5299 {
5300     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
5301
5302     if(!graphics)
5303         return InvalidParameter;
5304
5305     if(graphics->busy)
5306         return ObjectBusy;
5307
5308     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
5309 }
5310
5311
5312 /*****************************************************************************
5313  * GdipMeasureDriverString [GDIPLUS.@]
5314  */
5315 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5316                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
5317                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
5318 {
5319     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
5320     return NotImplemented;
5321 }
5322
5323 /*****************************************************************************
5324  * GdipDrawDriverString [GDIPLUS.@]
5325  */
5326 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5327                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
5328                                          GDIPCONST PointF *positions, INT flags,
5329                                          GDIPCONST GpMatrix *matrix )
5330 {
5331     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
5332     return NotImplemented;
5333 }
5334
5335 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
5336                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
5337 {
5338     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
5339     return NotImplemented;
5340 }
5341
5342 /*****************************************************************************
5343  * GdipRecordMetafileI [GDIPLUS.@]
5344  */
5345 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
5346                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
5347 {
5348     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
5349     return NotImplemented;
5350 }
5351
5352 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
5353                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
5354 {
5355     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
5356     return NotImplemented;
5357 }
5358
5359 /*****************************************************************************
5360  * GdipIsVisibleClipEmpty [GDIPLUS.@]
5361  */
5362 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
5363 {
5364     GpStatus stat;
5365     GpRegion* rgn;
5366
5367     TRACE("(%p, %p)\n", graphics, res);
5368
5369     if((stat = GdipCreateRegion(&rgn)) != Ok)
5370         return stat;
5371
5372     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
5373         goto cleanup;
5374
5375     stat = GdipIsEmptyRegion(rgn, graphics, res);
5376
5377 cleanup:
5378     GdipDeleteRegion(rgn);
5379     return stat;
5380 }
5381
5382 GpStatus WINGDIPAPI GdipGetHemfFromMetafile(GpMetafile *metafile, HENHMETAFILE *hEmf)
5383 {
5384     FIXME("(%p,%p): stub\n", metafile, hEmf);
5385
5386     if (!metafile || !hEmf)
5387         return InvalidParameter;
5388
5389     *hEmf = NULL;
5390
5391     return NotImplemented;
5392 }