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