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