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