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