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