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