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