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