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