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