ntdll: Make LdrLoadDll hotpatchable.
[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     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
2532     return NotImplemented;
2533 }
2534
2535 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
2536     GDIPCONST GpPoint *dstpoints, INT count)
2537 {
2538     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
2539     return NotImplemented;
2540 }
2541
2542 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
2543      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
2544      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2545      DrawImageAbort callback, VOID * callbackData)
2546 {
2547     GpPointF ptf[4];
2548     POINT pti[4];
2549     REAL dx, dy;
2550     GpStatus stat;
2551
2552     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
2553           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2554           callbackData);
2555
2556     if (count > 3)
2557         return NotImplemented;
2558
2559     if(!graphics || !image || !points || count != 3)
2560          return InvalidParameter;
2561
2562     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
2563         debugstr_pointf(&points[2]));
2564
2565     memcpy(ptf, points, 3 * sizeof(GpPointF));
2566     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
2567     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
2568     if (!srcwidth || !srcheight || ptf[3].X == ptf[0].X || ptf[3].Y == ptf[0].Y)
2569         return Ok;
2570     transform_and_round_points(graphics, pti, ptf, 4);
2571
2572     if (image->picture)
2573     {
2574         if (!graphics->hdc)
2575         {
2576             FIXME("graphics object has no HDC\n");
2577         }
2578
2579         /* FIXME: partially implemented (only works for rectangular parallelograms) */
2580         if(srcUnit == UnitInch)
2581             dx = dy = (REAL) INCH_HIMETRIC;
2582         else if(srcUnit == UnitPixel){
2583             dx = ((REAL) INCH_HIMETRIC) /
2584                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
2585             dy = ((REAL) INCH_HIMETRIC) /
2586                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
2587         }
2588         else
2589             return NotImplemented;
2590
2591         if(IPicture_Render(image->picture, graphics->hdc,
2592             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
2593             srcx * dx, srcy * dy,
2594             srcwidth * dx, srcheight * dy,
2595             NULL) != S_OK){
2596             if(callback)
2597                 callback(callbackData);
2598             return GenericError;
2599         }
2600     }
2601     else if (image->type == ImageTypeBitmap)
2602     {
2603         GpBitmap* bitmap = (GpBitmap*)image;
2604         int use_software=0;
2605
2606         if (srcUnit == UnitInch)
2607             dx = dy = 96.0; /* FIXME: use the image resolution */
2608         else if (srcUnit == UnitPixel)
2609             dx = dy = 1.0;
2610         else
2611             return NotImplemented;
2612
2613         srcx = srcx * dx;
2614         srcy = srcy * dy;
2615         srcwidth = srcwidth * dx;
2616         srcheight = srcheight * dy;
2617
2618         if (imageAttributes ||
2619             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
2620             !((GpBitmap*)image)->hbitmap ||
2621             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X ||
2622             ptf[1].X - ptf[0].X != srcwidth || ptf[2].Y - ptf[0].Y != srcheight ||
2623             srcx < 0 || srcy < 0 ||
2624             srcx + srcwidth > bitmap->width || srcy + srcheight > bitmap->height)
2625             use_software = 1;
2626
2627         if (use_software)
2628         {
2629             RECT dst_area;
2630             GpRect src_area;
2631             int i, x, y, src_stride, dst_stride;
2632             GpMatrix *dst_to_src;
2633             REAL m11, m12, m21, m22, mdx, mdy;
2634             LPBYTE src_data, dst_data;
2635             BitmapData lockeddata;
2636             InterpolationMode interpolation = graphics->interpolation;
2637             GpPointF dst_to_src_points[3] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}};
2638             REAL x_dx, x_dy, y_dx, y_dy;
2639             static const GpImageAttributes defaultImageAttributes = {WrapModeClamp, 0, FALSE};
2640
2641             if (!imageAttributes)
2642                 imageAttributes = &defaultImageAttributes;
2643
2644             dst_area.left = dst_area.right = pti[0].x;
2645             dst_area.top = dst_area.bottom = pti[0].y;
2646             for (i=1; i<4; i++)
2647             {
2648                 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2649                 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2650                 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2651                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2652             }
2653
2654             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2655             m21 = (ptf[2].X - ptf[0].X) / srcheight;
2656             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2657             m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2658             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2659             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2660
2661             stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
2662             if (stat != Ok) return stat;
2663
2664             stat = GdipInvertMatrix(dst_to_src);
2665             if (stat != Ok)
2666             {
2667                 GdipDeleteMatrix(dst_to_src);
2668                 return stat;
2669             }
2670
2671             dst_data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
2672             if (!dst_data)
2673             {
2674                 GdipDeleteMatrix(dst_to_src);
2675                 return OutOfMemory;
2676             }
2677
2678             dst_stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
2679
2680             get_bitmap_sample_size(interpolation, imageAttributes->wrap,
2681                 bitmap, srcx, srcy, srcwidth, srcheight, &src_area);
2682
2683             src_data = GdipAlloc(sizeof(ARGB) * src_area.Width * src_area.Height);
2684             if (!src_data)
2685             {
2686                 GdipFree(dst_data);
2687                 GdipDeleteMatrix(dst_to_src);
2688                 return OutOfMemory;
2689             }
2690             src_stride = sizeof(ARGB) * src_area.Width;
2691
2692             /* Read the bits we need from the source bitmap into an ARGB buffer. */
2693             lockeddata.Width = src_area.Width;
2694             lockeddata.Height = src_area.Height;
2695             lockeddata.Stride = src_stride;
2696             lockeddata.PixelFormat = PixelFormat32bppARGB;
2697             lockeddata.Scan0 = src_data;
2698
2699             stat = GdipBitmapLockBits(bitmap, &src_area, ImageLockModeRead|ImageLockModeUserInputBuf,
2700                 PixelFormat32bppARGB, &lockeddata);
2701
2702             if (stat == Ok)
2703                 stat = GdipBitmapUnlockBits(bitmap, &lockeddata);
2704
2705             if (stat != Ok)
2706             {
2707                 if (src_data != dst_data)
2708                     GdipFree(src_data);
2709                 GdipFree(dst_data);
2710                 GdipDeleteMatrix(dst_to_src);
2711                 return OutOfMemory;
2712             }
2713
2714             apply_image_attributes(imageAttributes, src_data,
2715                 src_area.Width, src_area.Height,
2716                 src_stride, ColorAdjustTypeBitmap);
2717
2718             /* Transform the bits as needed to the destination. */
2719             GdipTransformMatrixPoints(dst_to_src, dst_to_src_points, 3);
2720
2721             x_dx = dst_to_src_points[1].X - dst_to_src_points[0].X;
2722             x_dy = dst_to_src_points[1].Y - dst_to_src_points[0].Y;
2723             y_dx = dst_to_src_points[2].X - dst_to_src_points[0].X;
2724             y_dy = dst_to_src_points[2].Y - dst_to_src_points[0].Y;
2725
2726             for (x=dst_area.left; x<dst_area.right; x++)
2727             {
2728                 for (y=dst_area.top; y<dst_area.bottom; y++)
2729                 {
2730                     GpPointF src_pointf;
2731                     ARGB *dst_color;
2732
2733                     src_pointf.X = dst_to_src_points[0].X + x * x_dx + y * y_dx;
2734                     src_pointf.Y = dst_to_src_points[0].Y + x * x_dy + y * y_dy;
2735
2736                     dst_color = (ARGB*)(dst_data + dst_stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2737
2738                     if (src_pointf.X >= srcx && src_pointf.X < srcx + srcwidth && src_pointf.Y >= srcy && src_pointf.Y < srcy+srcheight)
2739                         *dst_color = resample_bitmap_pixel(&src_area, src_data, bitmap->width, bitmap->height, &src_pointf, imageAttributes, interpolation);
2740                     else
2741                         *dst_color = 0;
2742                 }
2743             }
2744
2745             GdipDeleteMatrix(dst_to_src);
2746
2747             GdipFree(src_data);
2748
2749             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
2750                 dst_data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, dst_stride);
2751
2752             GdipFree(dst_data);
2753
2754             return stat;
2755         }
2756         else
2757         {
2758             HDC hdc;
2759             int temp_hdc=0, temp_bitmap=0;
2760             HBITMAP hbitmap, old_hbm=NULL;
2761
2762             if (!(bitmap->format == PixelFormat16bppRGB555 ||
2763                   bitmap->format == PixelFormat24bppRGB ||
2764                   bitmap->format == PixelFormat32bppRGB ||
2765                   bitmap->format == PixelFormat32bppPARGB))
2766             {
2767                 BITMAPINFOHEADER bih;
2768                 BYTE *temp_bits;
2769                 PixelFormat dst_format;
2770
2771                 /* we can't draw a bitmap of this format directly */
2772                 hdc = CreateCompatibleDC(0);
2773                 temp_hdc = 1;
2774                 temp_bitmap = 1;
2775
2776                 bih.biSize = sizeof(BITMAPINFOHEADER);
2777                 bih.biWidth = bitmap->width;
2778                 bih.biHeight = -bitmap->height;
2779                 bih.biPlanes = 1;
2780                 bih.biBitCount = 32;
2781                 bih.biCompression = BI_RGB;
2782                 bih.biSizeImage = 0;
2783                 bih.biXPelsPerMeter = 0;
2784                 bih.biYPelsPerMeter = 0;
2785                 bih.biClrUsed = 0;
2786                 bih.biClrImportant = 0;
2787
2788                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
2789                     (void**)&temp_bits, NULL, 0);
2790
2791                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2792                     dst_format = PixelFormat32bppPARGB;
2793                 else
2794                     dst_format = PixelFormat32bppRGB;
2795
2796                 convert_pixels(bitmap->width, bitmap->height,
2797                     bitmap->width*4, temp_bits, dst_format,
2798                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
2799             }
2800             else
2801             {
2802                 hbitmap = bitmap->hbitmap;
2803                 hdc = bitmap->hdc;
2804                 temp_hdc = (hdc == 0);
2805             }
2806
2807             if (temp_hdc)
2808             {
2809                 if (!hdc) hdc = CreateCompatibleDC(0);
2810                 old_hbm = SelectObject(hdc, hbitmap);
2811             }
2812
2813             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2814             {
2815                 BLENDFUNCTION bf;
2816
2817                 bf.BlendOp = AC_SRC_OVER;
2818                 bf.BlendFlags = 0;
2819                 bf.SourceConstantAlpha = 255;
2820                 bf.AlphaFormat = AC_SRC_ALPHA;
2821
2822                 GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2823                     hdc, srcx, srcy, srcwidth, srcheight, bf);
2824             }
2825             else
2826             {
2827                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2828                     hdc, srcx, srcy, srcwidth, srcheight, SRCCOPY);
2829             }
2830
2831             if (temp_hdc)
2832             {
2833                 SelectObject(hdc, old_hbm);
2834                 DeleteDC(hdc);
2835             }
2836
2837             if (temp_bitmap)
2838                 DeleteObject(hbitmap);
2839         }
2840     }
2841     else
2842     {
2843         ERR("GpImage with no IPicture or HBITMAP?!\n");
2844         return NotImplemented;
2845     }
2846
2847     return Ok;
2848 }
2849
2850 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
2851      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
2852      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2853      DrawImageAbort callback, VOID * callbackData)
2854 {
2855     GpPointF pointsF[3];
2856     INT i;
2857
2858     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
2859           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2860           callbackData);
2861
2862     if(!points || count!=3)
2863         return InvalidParameter;
2864
2865     for(i = 0; i < count; i++){
2866         pointsF[i].X = (REAL)points[i].X;
2867         pointsF[i].Y = (REAL)points[i].Y;
2868     }
2869
2870     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
2871                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
2872                                    callback, callbackData);
2873 }
2874
2875 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
2876     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
2877     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
2878     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
2879     VOID * callbackData)
2880 {
2881     GpPointF points[3];
2882
2883     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
2884           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2885           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2886
2887     points[0].X = dstx;
2888     points[0].Y = dsty;
2889     points[1].X = dstx + dstwidth;
2890     points[1].Y = dsty;
2891     points[2].X = dstx;
2892     points[2].Y = dsty + dstheight;
2893
2894     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2895                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2896 }
2897
2898 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
2899         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
2900         INT srcwidth, INT srcheight, GpUnit srcUnit,
2901         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
2902         VOID * callbackData)
2903 {
2904     GpPointF points[3];
2905
2906     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
2907           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2908           srcwidth, srcheight, srcUnit, imageAttributes, 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, imageAttributes, callback, callbackData);
2919 }
2920
2921 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
2922     REAL x, REAL y, REAL width, REAL height)
2923 {
2924     RectF bounds;
2925     GpUnit unit;
2926     GpStatus ret;
2927
2928     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
2929
2930     if(!graphics || !image)
2931         return InvalidParameter;
2932
2933     ret = GdipGetImageBounds(image, &bounds, &unit);
2934     if(ret != Ok)
2935         return ret;
2936
2937     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
2938                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
2939                                  unit, NULL, NULL, NULL);
2940 }
2941
2942 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
2943     INT x, INT y, INT width, INT height)
2944 {
2945     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
2946
2947     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
2948 }
2949
2950 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
2951     REAL y1, REAL x2, REAL y2)
2952 {
2953     INT save_state;
2954     GpPointF pt[2];
2955     GpStatus retval;
2956
2957     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
2958
2959     if(!pen || !graphics)
2960         return InvalidParameter;
2961
2962     if(graphics->busy)
2963         return ObjectBusy;
2964
2965     if (!graphics->hdc)
2966     {
2967         FIXME("graphics object has no HDC\n");
2968         return Ok;
2969     }
2970
2971     pt[0].X = x1;
2972     pt[0].Y = y1;
2973     pt[1].X = x2;
2974     pt[1].Y = y2;
2975
2976     save_state = prepare_dc(graphics, pen);
2977
2978     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2979
2980     restore_dc(graphics, save_state);
2981
2982     return retval;
2983 }
2984
2985 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
2986     INT y1, INT x2, INT y2)
2987 {
2988     INT save_state;
2989     GpPointF pt[2];
2990     GpStatus retval;
2991
2992     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
2993
2994     if(!pen || !graphics)
2995         return InvalidParameter;
2996
2997     if(graphics->busy)
2998         return ObjectBusy;
2999
3000     if (!graphics->hdc)
3001     {
3002         FIXME("graphics object has no HDC\n");
3003         return Ok;
3004     }
3005
3006     pt[0].X = (REAL)x1;
3007     pt[0].Y = (REAL)y1;
3008     pt[1].X = (REAL)x2;
3009     pt[1].Y = (REAL)y2;
3010
3011     save_state = prepare_dc(graphics, pen);
3012
3013     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
3014
3015     restore_dc(graphics, save_state);
3016
3017     return retval;
3018 }
3019
3020 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
3021     GpPointF *points, INT count)
3022 {
3023     INT save_state;
3024     GpStatus retval;
3025
3026     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3027
3028     if(!pen || !graphics || (count < 2))
3029         return InvalidParameter;
3030
3031     if(graphics->busy)
3032         return ObjectBusy;
3033
3034     if (!graphics->hdc)
3035     {
3036         FIXME("graphics object has no HDC\n");
3037         return Ok;
3038     }
3039
3040     save_state = prepare_dc(graphics, pen);
3041
3042     retval = draw_polyline(graphics, pen, points, count, TRUE);
3043
3044     restore_dc(graphics, save_state);
3045
3046     return retval;
3047 }
3048
3049 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
3050     GpPoint *points, INT count)
3051 {
3052     INT save_state;
3053     GpStatus retval;
3054     GpPointF *ptf = NULL;
3055     int i;
3056
3057     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3058
3059     if(!pen || !graphics || (count < 2))
3060         return InvalidParameter;
3061
3062     if(graphics->busy)
3063         return ObjectBusy;
3064
3065     if (!graphics->hdc)
3066     {
3067         FIXME("graphics object has no HDC\n");
3068         return Ok;
3069     }
3070
3071     ptf = GdipAlloc(count * sizeof(GpPointF));
3072     if(!ptf) return OutOfMemory;
3073
3074     for(i = 0; i < count; i ++){
3075         ptf[i].X = (REAL) points[i].X;
3076         ptf[i].Y = (REAL) points[i].Y;
3077     }
3078
3079     save_state = prepare_dc(graphics, pen);
3080
3081     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
3082
3083     restore_dc(graphics, save_state);
3084
3085     GdipFree(ptf);
3086     return retval;
3087 }
3088
3089 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
3090 {
3091     INT save_state;
3092     GpStatus retval;
3093
3094     TRACE("(%p, %p, %p)\n", graphics, pen, path);
3095
3096     if(!pen || !graphics)
3097         return InvalidParameter;
3098
3099     if(graphics->busy)
3100         return ObjectBusy;
3101
3102     if (!graphics->hdc)
3103     {
3104         FIXME("graphics object has no HDC\n");
3105         return Ok;
3106     }
3107
3108     save_state = prepare_dc(graphics, pen);
3109
3110     retval = draw_poly(graphics, pen, path->pathdata.Points,
3111                        path->pathdata.Types, path->pathdata.Count, TRUE);
3112
3113     restore_dc(graphics, save_state);
3114
3115     return retval;
3116 }
3117
3118 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
3119     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3120 {
3121     INT save_state;
3122
3123     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
3124             width, height, startAngle, sweepAngle);
3125
3126     if(!graphics || !pen)
3127         return InvalidParameter;
3128
3129     if(graphics->busy)
3130         return ObjectBusy;
3131
3132     if (!graphics->hdc)
3133     {
3134         FIXME("graphics object has no HDC\n");
3135         return Ok;
3136     }
3137
3138     save_state = prepare_dc(graphics, pen);
3139     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3140
3141     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
3142
3143     restore_dc(graphics, save_state);
3144
3145     return Ok;
3146 }
3147
3148 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
3149     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3150 {
3151     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
3152             width, height, startAngle, sweepAngle);
3153
3154     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3155 }
3156
3157 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
3158     REAL y, REAL width, REAL height)
3159 {
3160     INT save_state;
3161     GpPointF ptf[4];
3162     POINT pti[4];
3163
3164     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
3165
3166     if(!pen || !graphics)
3167         return InvalidParameter;
3168
3169     if(graphics->busy)
3170         return ObjectBusy;
3171
3172     if (!graphics->hdc)
3173     {
3174         FIXME("graphics object has no HDC\n");
3175         return Ok;
3176     }
3177
3178     ptf[0].X = x;
3179     ptf[0].Y = y;
3180     ptf[1].X = x + width;
3181     ptf[1].Y = y;
3182     ptf[2].X = x + width;
3183     ptf[2].Y = y + height;
3184     ptf[3].X = x;
3185     ptf[3].Y = y + height;
3186
3187     save_state = prepare_dc(graphics, pen);
3188     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3189
3190     transform_and_round_points(graphics, pti, ptf, 4);
3191     Polygon(graphics->hdc, pti, 4);
3192
3193     restore_dc(graphics, save_state);
3194
3195     return Ok;
3196 }
3197
3198 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
3199     INT y, INT width, INT height)
3200 {
3201     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
3202
3203     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3204 }
3205
3206 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
3207     GDIPCONST GpRectF* rects, INT count)
3208 {
3209     GpPointF *ptf;
3210     POINT *pti;
3211     INT save_state, i;
3212
3213     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3214
3215     if(!graphics || !pen || !rects || count < 1)
3216         return InvalidParameter;
3217
3218     if(graphics->busy)
3219         return ObjectBusy;
3220
3221     if (!graphics->hdc)
3222     {
3223         FIXME("graphics object has no HDC\n");
3224         return Ok;
3225     }
3226
3227     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
3228     pti = GdipAlloc(4 * count * sizeof(POINT));
3229
3230     if(!ptf || !pti){
3231         GdipFree(ptf);
3232         GdipFree(pti);
3233         return OutOfMemory;
3234     }
3235
3236     for(i = 0; i < count; i++){
3237         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
3238         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
3239         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
3240         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
3241     }
3242
3243     save_state = prepare_dc(graphics, pen);
3244     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3245
3246     transform_and_round_points(graphics, pti, ptf, 4 * count);
3247
3248     for(i = 0; i < count; i++)
3249         Polygon(graphics->hdc, &pti[4 * i], 4);
3250
3251     restore_dc(graphics, save_state);
3252
3253     GdipFree(ptf);
3254     GdipFree(pti);
3255
3256     return Ok;
3257 }
3258
3259 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
3260     GDIPCONST GpRect* rects, INT count)
3261 {
3262     GpRectF *rectsF;
3263     GpStatus ret;
3264     INT i;
3265
3266     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
3267
3268     if(!rects || count<=0)
3269         return InvalidParameter;
3270
3271     rectsF = GdipAlloc(sizeof(GpRectF) * count);
3272     if(!rectsF)
3273         return OutOfMemory;
3274
3275     for(i = 0;i < count;i++){
3276         rectsF[i].X      = (REAL)rects[i].X;
3277         rectsF[i].Y      = (REAL)rects[i].Y;
3278         rectsF[i].Width  = (REAL)rects[i].Width;
3279         rectsF[i].Height = (REAL)rects[i].Height;
3280     }
3281
3282     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
3283     GdipFree(rectsF);
3284
3285     return ret;
3286 }
3287
3288 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
3289     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
3290 {
3291     GpPath *path;
3292     GpStatus stat;
3293
3294     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3295             count, tension, fill);
3296
3297     if(!graphics || !brush || !points)
3298         return InvalidParameter;
3299
3300     if(graphics->busy)
3301         return ObjectBusy;
3302
3303     if(count == 1)    /* Do nothing */
3304         return Ok;
3305
3306     stat = GdipCreatePath(fill, &path);
3307     if(stat != Ok)
3308         return stat;
3309
3310     stat = GdipAddPathClosedCurve2(path, points, count, tension);
3311     if(stat != Ok){
3312         GdipDeletePath(path);
3313         return stat;
3314     }
3315
3316     stat = GdipFillPath(graphics, brush, path);
3317     if(stat != Ok){
3318         GdipDeletePath(path);
3319         return stat;
3320     }
3321
3322     GdipDeletePath(path);
3323
3324     return Ok;
3325 }
3326
3327 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
3328     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
3329 {
3330     GpPointF *ptf;
3331     GpStatus stat;
3332     INT i;
3333
3334     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
3335             count, tension, fill);
3336
3337     if(!points || count == 0)
3338         return InvalidParameter;
3339
3340     if(count == 1)    /* Do nothing */
3341         return Ok;
3342
3343     ptf = GdipAlloc(sizeof(GpPointF)*count);
3344     if(!ptf)
3345         return OutOfMemory;
3346
3347     for(i = 0;i < count;i++){
3348         ptf[i].X = (REAL)points[i].X;
3349         ptf[i].Y = (REAL)points[i].Y;
3350     }
3351
3352     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
3353
3354     GdipFree(ptf);
3355
3356     return stat;
3357 }
3358
3359 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
3360     GDIPCONST GpPointF *points, INT count)
3361 {
3362     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3363     return GdipFillClosedCurve2(graphics, brush, points, count,
3364                0.5f, FillModeAlternate);
3365 }
3366
3367 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
3368     GDIPCONST GpPoint *points, INT count)
3369 {
3370     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3371     return GdipFillClosedCurve2I(graphics, brush, points, count,
3372                0.5f, FillModeAlternate);
3373 }
3374
3375 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
3376     REAL y, REAL width, REAL height)
3377 {
3378     GpStatus stat;
3379     GpPath *path;
3380
3381     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3382
3383     if(!graphics || !brush)
3384         return InvalidParameter;
3385
3386     if(graphics->busy)
3387         return ObjectBusy;
3388
3389     stat = GdipCreatePath(FillModeAlternate, &path);
3390
3391     if (stat == Ok)
3392     {
3393         stat = GdipAddPathEllipse(path, x, y, width, height);
3394
3395         if (stat == Ok)
3396             stat = GdipFillPath(graphics, brush, path);
3397
3398         GdipDeletePath(path);
3399     }
3400
3401     return stat;
3402 }
3403
3404 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
3405     INT y, INT width, INT height)
3406 {
3407     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3408
3409     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
3410 }
3411
3412 static GpStatus GDI32_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3413 {
3414     INT save_state;
3415     GpStatus retval;
3416
3417     if(!graphics->hdc || !brush_can_fill_path(brush))
3418         return NotImplemented;
3419
3420     save_state = SaveDC(graphics->hdc);
3421     EndPath(graphics->hdc);
3422     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
3423                                                                     : WINDING));
3424
3425     BeginPath(graphics->hdc);
3426     retval = draw_poly(graphics, NULL, path->pathdata.Points,
3427                        path->pathdata.Types, path->pathdata.Count, FALSE);
3428
3429     if(retval != Ok)
3430         goto end;
3431
3432     EndPath(graphics->hdc);
3433     brush_fill_path(graphics, brush);
3434
3435     retval = Ok;
3436
3437 end:
3438     RestoreDC(graphics->hdc, save_state);
3439
3440     return retval;
3441 }
3442
3443 static GpStatus SOFTWARE_GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3444 {
3445     GpStatus stat;
3446     GpRegion *rgn;
3447
3448     if (!brush_can_fill_pixels(brush))
3449         return NotImplemented;
3450
3451     /* FIXME: This could probably be done more efficiently without regions. */
3452
3453     stat = GdipCreateRegionPath(path, &rgn);
3454
3455     if (stat == Ok)
3456     {
3457         stat = GdipFillRegion(graphics, brush, rgn);
3458
3459         GdipDeleteRegion(rgn);
3460     }
3461
3462     return stat;
3463 }
3464
3465 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
3466 {
3467     GpStatus stat = NotImplemented;
3468
3469     TRACE("(%p, %p, %p)\n", graphics, brush, path);
3470
3471     if(!brush || !graphics || !path)
3472         return InvalidParameter;
3473
3474     if(graphics->busy)
3475         return ObjectBusy;
3476
3477     if (!graphics->image)
3478         stat = GDI32_GdipFillPath(graphics, brush, path);
3479
3480     if (stat == NotImplemented)
3481         stat = SOFTWARE_GdipFillPath(graphics, brush, path);
3482
3483     if (stat == NotImplemented)
3484     {
3485         FIXME("Not implemented for brushtype %i\n", brush->bt);
3486         stat = Ok;
3487     }
3488
3489     return stat;
3490 }
3491
3492 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
3493     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
3494 {
3495     GpStatus stat;
3496     GpPath *path;
3497
3498     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
3499             graphics, brush, x, y, width, height, startAngle, sweepAngle);
3500
3501     if(!graphics || !brush)
3502         return InvalidParameter;
3503
3504     if(graphics->busy)
3505         return ObjectBusy;
3506
3507     stat = GdipCreatePath(FillModeAlternate, &path);
3508
3509     if (stat == Ok)
3510     {
3511         stat = GdipAddPathPie(path, x, y, width, height, startAngle, sweepAngle);
3512
3513         if (stat == Ok)
3514             stat = GdipFillPath(graphics, brush, path);
3515
3516         GdipDeletePath(path);
3517     }
3518
3519     return stat;
3520 }
3521
3522 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
3523     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
3524 {
3525     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
3526             graphics, brush, x, y, width, height, startAngle, sweepAngle);
3527
3528     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
3529 }
3530
3531 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
3532     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
3533 {
3534     GpStatus stat;
3535     GpPath *path;
3536
3537     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3538
3539     if(!graphics || !brush || !points || !count)
3540         return InvalidParameter;
3541
3542     if(graphics->busy)
3543         return ObjectBusy;
3544
3545     stat = GdipCreatePath(fillMode, &path);
3546
3547     if (stat == Ok)
3548     {
3549         stat = GdipAddPathPolygon(path, points, count);
3550
3551         if (stat == Ok)
3552             stat = GdipFillPath(graphics, brush, path);
3553
3554         GdipDeletePath(path);
3555     }
3556
3557     return stat;
3558 }
3559
3560 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
3561     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
3562 {
3563     GpStatus stat;
3564     GpPath *path;
3565
3566     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
3567
3568     if(!graphics || !brush || !points || !count)
3569         return InvalidParameter;
3570
3571     if(graphics->busy)
3572         return ObjectBusy;
3573
3574     stat = GdipCreatePath(fillMode, &path);
3575
3576     if (stat == Ok)
3577     {
3578         stat = GdipAddPathPolygonI(path, points, count);
3579
3580         if (stat == Ok)
3581             stat = GdipFillPath(graphics, brush, path);
3582
3583         GdipDeletePath(path);
3584     }
3585
3586     return stat;
3587 }
3588
3589 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
3590     GDIPCONST GpPointF *points, INT count)
3591 {
3592     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3593
3594     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
3595 }
3596
3597 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
3598     GDIPCONST GpPoint *points, INT count)
3599 {
3600     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3601
3602     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
3603 }
3604
3605 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
3606     REAL x, REAL y, REAL width, REAL height)
3607 {
3608     GpStatus stat;
3609     GpPath *path;
3610
3611     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3612
3613     if(!graphics || !brush)
3614         return InvalidParameter;
3615
3616     if(graphics->busy)
3617         return ObjectBusy;
3618
3619     stat = GdipCreatePath(FillModeAlternate, &path);
3620
3621     if (stat == Ok)
3622     {
3623         stat = GdipAddPathRectangle(path, x, y, width, height);
3624
3625         if (stat == Ok)
3626             stat = GdipFillPath(graphics, brush, path);
3627
3628         GdipDeletePath(path);
3629     }
3630
3631     return stat;
3632 }
3633
3634 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3635     INT x, INT y, INT width, INT height)
3636 {
3637     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3638
3639     return GdipFillRectangle(graphics, brush, x, y, width, height);
3640 }
3641
3642 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3643     INT count)
3644 {
3645     GpStatus ret;
3646     INT i;
3647
3648     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3649
3650     if(!rects)
3651         return InvalidParameter;
3652
3653     for(i = 0; i < count; i++){
3654         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3655         if(ret != Ok)   return ret;
3656     }
3657
3658     return Ok;
3659 }
3660
3661 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3662     INT count)
3663 {
3664     GpRectF *rectsF;
3665     GpStatus ret;
3666     INT i;
3667
3668     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3669
3670     if(!rects || count <= 0)
3671         return InvalidParameter;
3672
3673     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3674     if(!rectsF)
3675         return OutOfMemory;
3676
3677     for(i = 0; i < count; i++){
3678         rectsF[i].X      = (REAL)rects[i].X;
3679         rectsF[i].Y      = (REAL)rects[i].Y;
3680         rectsF[i].X      = (REAL)rects[i].Width;
3681         rectsF[i].Height = (REAL)rects[i].Height;
3682     }
3683
3684     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3685     GdipFree(rectsF);
3686
3687     return ret;
3688 }
3689
3690 static GpStatus GDI32_GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3691     GpRegion* region)
3692 {
3693     INT save_state;
3694     GpStatus status;
3695     HRGN hrgn;
3696     RECT rc;
3697
3698     if(!graphics->hdc || !brush_can_fill_path(brush))
3699         return NotImplemented;
3700
3701     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3702     if(status != Ok)
3703         return status;
3704
3705     save_state = SaveDC(graphics->hdc);
3706     EndPath(graphics->hdc);
3707
3708     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3709
3710     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3711     {
3712         BeginPath(graphics->hdc);
3713         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3714         EndPath(graphics->hdc);
3715
3716         brush_fill_path(graphics, brush);
3717     }
3718
3719     RestoreDC(graphics->hdc, save_state);
3720
3721     DeleteObject(hrgn);
3722
3723     return Ok;
3724 }
3725
3726 static GpStatus SOFTWARE_GdipFillRegion(GpGraphics *graphics, GpBrush *brush,
3727     GpRegion* region)
3728 {
3729     GpStatus stat;
3730     GpRegion *temp_region;
3731     GpMatrix *world_to_device, *identity;
3732     GpRectF graphics_bounds;
3733     UINT scans_count, i;
3734     INT dummy;
3735     GpRect *scans = NULL;
3736     DWORD *pixel_data;
3737
3738     if (!brush_can_fill_pixels(brush))
3739         return NotImplemented;
3740
3741     stat = get_graphics_bounds(graphics, &graphics_bounds);
3742
3743     if (stat == Ok)
3744         stat = GdipCloneRegion(region, &temp_region);
3745
3746     if (stat == Ok)
3747     {
3748         stat = get_graphics_transform(graphics, CoordinateSpaceDevice,
3749             CoordinateSpaceWorld, &world_to_device);
3750
3751         if (stat == Ok)
3752         {
3753             stat = GdipTransformRegion(temp_region, world_to_device);
3754
3755             GdipDeleteMatrix(world_to_device);
3756         }
3757
3758         if (stat == Ok)
3759             stat = GdipCombineRegionRect(temp_region, &graphics_bounds, CombineModeIntersect);
3760
3761         if (stat == Ok)
3762             stat = GdipCreateMatrix(&identity);
3763
3764         if (stat == Ok)
3765         {
3766             stat = GdipGetRegionScansCount(temp_region, &scans_count, identity);
3767
3768             if (stat == Ok && scans_count != 0)
3769             {
3770                 scans = GdipAlloc(sizeof(*scans) * scans_count);
3771                 if (!scans)
3772                     stat = OutOfMemory;
3773
3774                 if (stat == Ok)
3775                 {
3776                     stat = GdipGetRegionScansI(temp_region, scans, &dummy, identity);
3777
3778                     if (stat != Ok)
3779                         GdipFree(scans);
3780                 }
3781             }
3782
3783             GdipDeleteMatrix(identity);
3784         }
3785
3786         GdipDeleteRegion(temp_region);
3787     }
3788
3789     if (stat == Ok && scans_count == 0)
3790         return Ok;
3791
3792     if (stat == Ok)
3793     {
3794         if (!graphics->image)
3795         {
3796             /* If we have to go through gdi32, use as few alpha blends as possible. */
3797             INT min_x, min_y, max_x, max_y;
3798             UINT data_width, data_height;
3799
3800             min_x = scans[0].X;
3801             min_y = scans[0].Y;
3802             max_x = scans[0].X+scans[0].Width;
3803             max_y = scans[0].Y+scans[0].Height;
3804
3805             for (i=1; i<scans_count; i++)
3806             {
3807                 min_x = min(min_x, scans[i].X);
3808                 min_y = min(min_y, scans[i].Y);
3809                 max_x = max(max_x, scans[i].X+scans[i].Width);
3810                 max_y = max(max_y, scans[i].Y+scans[i].Height);
3811             }
3812
3813             data_width = max_x - min_x;
3814             data_height = max_y - min_y;
3815
3816             pixel_data = GdipAlloc(sizeof(*pixel_data) * data_width * data_height);
3817             if (!pixel_data)
3818                 stat = OutOfMemory;
3819
3820             if (stat == Ok)
3821             {
3822                 for (i=0; i<scans_count; i++)
3823                 {
3824                     stat = brush_fill_pixels(graphics, brush,
3825                         pixel_data + (scans[i].X - min_x) + (scans[i].Y - min_y) * data_width,
3826                         &scans[i], data_width);
3827
3828                     if (stat != Ok)
3829                         break;
3830                 }
3831
3832                 if (stat == Ok)
3833                 {
3834                     stat = alpha_blend_pixels(graphics, min_x, min_y,
3835                         (BYTE*)pixel_data, data_width, data_height,
3836                         data_width * 4);
3837                 }
3838
3839                 GdipFree(pixel_data);
3840             }
3841         }
3842         else
3843         {
3844             UINT max_size=0;
3845
3846             for (i=0; i<scans_count; i++)
3847             {
3848                 UINT size = scans[i].Width * scans[i].Height;
3849
3850                 if (size > max_size)
3851                     max_size = size;
3852             }
3853
3854             pixel_data = GdipAlloc(sizeof(*pixel_data) * max_size);
3855             if (!pixel_data)
3856                 stat = OutOfMemory;
3857
3858             if (stat == Ok)
3859             {
3860                 for (i=0; i<scans_count; i++)
3861                 {
3862                     stat = brush_fill_pixels(graphics, brush, pixel_data, &scans[i],
3863                         scans[i].Width);
3864
3865                     if (stat == Ok)
3866                     {
3867                         stat = alpha_blend_pixels(graphics, scans[i].X, scans[i].Y,
3868                             (BYTE*)pixel_data, scans[i].Width, scans[i].Height,
3869                             scans[i].Width * 4);
3870                     }
3871
3872                     if (stat != Ok)
3873                         break;
3874                 }
3875
3876                 GdipFree(pixel_data);
3877             }
3878         }
3879
3880         GdipFree(scans);
3881     }
3882
3883     return stat;
3884 }
3885
3886 /*****************************************************************************
3887  * GdipFillRegion [GDIPLUS.@]
3888  */
3889 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3890         GpRegion* region)
3891 {
3892     GpStatus stat = NotImplemented;
3893
3894     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3895
3896     if (!(graphics && brush && region))
3897         return InvalidParameter;
3898
3899     if(graphics->busy)
3900         return ObjectBusy;
3901
3902     if (!graphics->image)
3903         stat = GDI32_GdipFillRegion(graphics, brush, region);
3904
3905     if (stat == NotImplemented)
3906         stat = SOFTWARE_GdipFillRegion(graphics, brush, region);
3907
3908     if (stat == NotImplemented)
3909     {
3910         FIXME("not implemented for brushtype %i\n", brush->bt);
3911         stat = Ok;
3912     }
3913
3914     return stat;
3915 }
3916
3917 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3918 {
3919     TRACE("(%p,%u)\n", graphics, intention);
3920
3921     if(!graphics)
3922         return InvalidParameter;
3923
3924     if(graphics->busy)
3925         return ObjectBusy;
3926
3927     /* We have no internal operation queue, so there's no need to clear it. */
3928
3929     if (graphics->hdc)
3930         GdiFlush();
3931
3932     return Ok;
3933 }
3934
3935 /*****************************************************************************
3936  * GdipGetClipBounds [GDIPLUS.@]
3937  */
3938 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3939 {
3940     TRACE("(%p, %p)\n", graphics, rect);
3941
3942     if(!graphics)
3943         return InvalidParameter;
3944
3945     if(graphics->busy)
3946         return ObjectBusy;
3947
3948     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3949 }
3950
3951 /*****************************************************************************
3952  * GdipGetClipBoundsI [GDIPLUS.@]
3953  */
3954 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3955 {
3956     TRACE("(%p, %p)\n", graphics, rect);
3957
3958     if(!graphics)
3959         return InvalidParameter;
3960
3961     if(graphics->busy)
3962         return ObjectBusy;
3963
3964     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3965 }
3966
3967 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3968 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3969     CompositingMode *mode)
3970 {
3971     TRACE("(%p, %p)\n", graphics, mode);
3972
3973     if(!graphics || !mode)
3974         return InvalidParameter;
3975
3976     if(graphics->busy)
3977         return ObjectBusy;
3978
3979     *mode = graphics->compmode;
3980
3981     return Ok;
3982 }
3983
3984 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3985 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3986     CompositingQuality *quality)
3987 {
3988     TRACE("(%p, %p)\n", graphics, quality);
3989
3990     if(!graphics || !quality)
3991         return InvalidParameter;
3992
3993     if(graphics->busy)
3994         return ObjectBusy;
3995
3996     *quality = graphics->compqual;
3997
3998     return Ok;
3999 }
4000
4001 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
4002 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
4003     InterpolationMode *mode)
4004 {
4005     TRACE("(%p, %p)\n", graphics, mode);
4006
4007     if(!graphics || !mode)
4008         return InvalidParameter;
4009
4010     if(graphics->busy)
4011         return ObjectBusy;
4012
4013     *mode = graphics->interpolation;
4014
4015     return Ok;
4016 }
4017
4018 /* FIXME: Need to handle color depths less than 24bpp */
4019 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
4020 {
4021     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
4022
4023     if(!graphics || !argb)
4024         return InvalidParameter;
4025
4026     if(graphics->busy)
4027         return ObjectBusy;
4028
4029     return Ok;
4030 }
4031
4032 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
4033 {
4034     TRACE("(%p, %p)\n", graphics, scale);
4035
4036     if(!graphics || !scale)
4037         return InvalidParameter;
4038
4039     if(graphics->busy)
4040         return ObjectBusy;
4041
4042     *scale = graphics->scale;
4043
4044     return Ok;
4045 }
4046
4047 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
4048 {
4049     TRACE("(%p, %p)\n", graphics, unit);
4050
4051     if(!graphics || !unit)
4052         return InvalidParameter;
4053
4054     if(graphics->busy)
4055         return ObjectBusy;
4056
4057     *unit = graphics->unit;
4058
4059     return Ok;
4060 }
4061
4062 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
4063 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4064     *mode)
4065 {
4066     TRACE("(%p, %p)\n", graphics, mode);
4067
4068     if(!graphics || !mode)
4069         return InvalidParameter;
4070
4071     if(graphics->busy)
4072         return ObjectBusy;
4073
4074     *mode = graphics->pixeloffset;
4075
4076     return Ok;
4077 }
4078
4079 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
4080 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
4081 {
4082     TRACE("(%p, %p)\n", graphics, mode);
4083
4084     if(!graphics || !mode)
4085         return InvalidParameter;
4086
4087     if(graphics->busy)
4088         return ObjectBusy;
4089
4090     *mode = graphics->smoothing;
4091
4092     return Ok;
4093 }
4094
4095 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
4096 {
4097     TRACE("(%p, %p)\n", graphics, contrast);
4098
4099     if(!graphics || !contrast)
4100         return InvalidParameter;
4101
4102     *contrast = graphics->textcontrast;
4103
4104     return Ok;
4105 }
4106
4107 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
4108 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
4109     TextRenderingHint *hint)
4110 {
4111     TRACE("(%p, %p)\n", graphics, hint);
4112
4113     if(!graphics || !hint)
4114         return InvalidParameter;
4115
4116     if(graphics->busy)
4117         return ObjectBusy;
4118
4119     *hint = graphics->texthint;
4120
4121     return Ok;
4122 }
4123
4124 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
4125 {
4126     GpRegion *clip_rgn;
4127     GpStatus stat;
4128
4129     TRACE("(%p, %p)\n", graphics, rect);
4130
4131     if(!graphics || !rect)
4132         return InvalidParameter;
4133
4134     if(graphics->busy)
4135         return ObjectBusy;
4136
4137     /* intersect window and graphics clipping regions */
4138     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
4139         return stat;
4140
4141     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
4142         goto cleanup;
4143
4144     /* get bounds of the region */
4145     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
4146
4147 cleanup:
4148     GdipDeleteRegion(clip_rgn);
4149
4150     return stat;
4151 }
4152
4153 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
4154 {
4155     GpRectF rectf;
4156     GpStatus stat;
4157
4158     TRACE("(%p, %p)\n", graphics, rect);
4159
4160     if(!graphics || !rect)
4161         return InvalidParameter;
4162
4163     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
4164     {
4165         rect->X = roundr(rectf.X);
4166         rect->Y = roundr(rectf.Y);
4167         rect->Width  = roundr(rectf.Width);
4168         rect->Height = roundr(rectf.Height);
4169     }
4170
4171     return stat;
4172 }
4173
4174 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4175 {
4176     TRACE("(%p, %p)\n", graphics, matrix);
4177
4178     if(!graphics || !matrix)
4179         return InvalidParameter;
4180
4181     if(graphics->busy)
4182         return ObjectBusy;
4183
4184     *matrix = *graphics->worldtrans;
4185     return Ok;
4186 }
4187
4188 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
4189 {
4190     GpSolidFill *brush;
4191     GpStatus stat;
4192     GpRectF wnd_rect;
4193
4194     TRACE("(%p, %x)\n", graphics, color);
4195
4196     if(!graphics)
4197         return InvalidParameter;
4198
4199     if(graphics->busy)
4200         return ObjectBusy;
4201
4202     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
4203         return stat;
4204
4205     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
4206         GdipDeleteBrush((GpBrush*)brush);
4207         return stat;
4208     }
4209
4210     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
4211                                                  wnd_rect.Width, wnd_rect.Height);
4212
4213     GdipDeleteBrush((GpBrush*)brush);
4214
4215     return Ok;
4216 }
4217
4218 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
4219 {
4220     TRACE("(%p, %p)\n", graphics, res);
4221
4222     if(!graphics || !res)
4223         return InvalidParameter;
4224
4225     return GdipIsEmptyRegion(graphics->clip, graphics, res);
4226 }
4227
4228 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
4229 {
4230     GpStatus stat;
4231     GpRegion* rgn;
4232     GpPointF pt;
4233
4234     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
4235
4236     if(!graphics || !result)
4237         return InvalidParameter;
4238
4239     if(graphics->busy)
4240         return ObjectBusy;
4241
4242     pt.X = x;
4243     pt.Y = y;
4244     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4245                    CoordinateSpaceWorld, &pt, 1)) != Ok)
4246         return stat;
4247
4248     if((stat = GdipCreateRegion(&rgn)) != Ok)
4249         return stat;
4250
4251     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4252         goto cleanup;
4253
4254     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
4255
4256 cleanup:
4257     GdipDeleteRegion(rgn);
4258     return stat;
4259 }
4260
4261 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
4262 {
4263     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
4264 }
4265
4266 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
4267 {
4268     GpStatus stat;
4269     GpRegion* rgn;
4270     GpPointF pts[2];
4271
4272     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
4273
4274     if(!graphics || !result)
4275         return InvalidParameter;
4276
4277     if(graphics->busy)
4278         return ObjectBusy;
4279
4280     pts[0].X = x;
4281     pts[0].Y = y;
4282     pts[1].X = x + width;
4283     pts[1].Y = y + height;
4284
4285     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
4286                     CoordinateSpaceWorld, pts, 2)) != Ok)
4287         return stat;
4288
4289     pts[1].X -= pts[0].X;
4290     pts[1].Y -= pts[0].Y;
4291
4292     if((stat = GdipCreateRegion(&rgn)) != Ok)
4293         return stat;
4294
4295     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4296         goto cleanup;
4297
4298     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
4299
4300 cleanup:
4301     GdipDeleteRegion(rgn);
4302     return stat;
4303 }
4304
4305 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
4306 {
4307     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
4308 }
4309
4310 GpStatus gdip_format_string(HDC hdc,
4311     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4312     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4313     gdip_format_string_callback callback, void *user_data)
4314 {
4315     WCHAR* stringdup;
4316     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
4317         nheight, lineend, lineno = 0;
4318     RectF bounds;
4319     StringAlignment halign;
4320     GpStatus stat = Ok;
4321     SIZE size;
4322
4323     if(length == -1) length = lstrlenW(string);
4324
4325     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
4326     if(!stringdup) return OutOfMemory;
4327
4328     nwidth = roundr(rect->Width);
4329     nheight = roundr(rect->Height);
4330
4331     if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
4332     if (rect->Height >= INT_MAX || rect->Height < 0.5) nheight = INT_MAX;
4333
4334     for(i = 0, j = 0; i < length; i++){
4335         /* FIXME: This makes the indexes passed to callback inaccurate. */
4336         if(!isprintW(string[i]) && (string[i] != '\n'))
4337             continue;
4338
4339         stringdup[j] = string[i];
4340         j++;
4341     }
4342
4343     length = j;
4344
4345     if (format) halign = format->align;
4346     else halign = StringAlignmentNear;
4347
4348     while(sum < length){
4349         GetTextExtentExPointW(hdc, stringdup + sum, length - sum,
4350                               nwidth, &fit, NULL, &size);
4351         fitcpy = fit;
4352
4353         if(fit == 0)
4354             break;
4355
4356         for(lret = 0; lret < fit; lret++)
4357             if(*(stringdup + sum + lret) == '\n')
4358                 break;
4359
4360         /* Line break code (may look strange, but it imitates windows). */
4361         if(lret < fit)
4362             lineend = fit = lret;    /* this is not an off-by-one error */
4363         else if(fit < (length - sum)){
4364             if(*(stringdup + sum + fit) == ' ')
4365                 while(*(stringdup + sum + fit) == ' ')
4366                     fit++;
4367             else
4368                 while(*(stringdup + sum + fit - 1) != ' '){
4369                     fit--;
4370
4371                     if(*(stringdup + sum + fit) == '\t')
4372                         break;
4373
4374                     if(fit == 0){
4375                         fit = fitcpy;
4376                         break;
4377                     }
4378                 }
4379             lineend = fit;
4380             while(*(stringdup + sum + lineend - 1) == ' ' ||
4381                   *(stringdup + sum + lineend - 1) == '\t')
4382                 lineend--;
4383         }
4384         else
4385             lineend = fit;
4386
4387         GetTextExtentExPointW(hdc, stringdup + sum, lineend,
4388                               nwidth, &j, NULL, &size);
4389
4390         bounds.Width = size.cx;
4391
4392         if(height + size.cy > nheight)
4393             bounds.Height = nheight - (height + size.cy);
4394         else
4395             bounds.Height = size.cy;
4396
4397         bounds.Y = rect->Y + height;
4398
4399         switch (halign)
4400         {
4401         case StringAlignmentNear:
4402         default:
4403             bounds.X = rect->X;
4404             break;
4405         case StringAlignmentCenter:
4406             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
4407             break;
4408         case StringAlignmentFar:
4409             bounds.X = rect->X + rect->Width - bounds.Width;
4410             break;
4411         }
4412
4413         stat = callback(hdc, stringdup, sum, lineend,
4414             font, rect, format, lineno, &bounds, user_data);
4415
4416         if (stat != Ok)
4417             break;
4418
4419         sum += fit + (lret < fitcpy ? 1 : 0);
4420         height += size.cy;
4421         lineno++;
4422
4423         if(height > nheight)
4424             break;
4425
4426         /* Stop if this was a linewrap (but not if it was a linebreak). */
4427         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
4428             break;
4429     }
4430
4431     GdipFree(stringdup);
4432
4433     return stat;
4434 }
4435
4436 struct measure_ranges_args {
4437     GpRegion **regions;
4438 };
4439
4440 static GpStatus measure_ranges_callback(HDC hdc,
4441     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4442     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4443     INT lineno, const RectF *bounds, void *user_data)
4444 {
4445     int i;
4446     GpStatus stat = Ok;
4447     struct measure_ranges_args *args = user_data;
4448
4449     for (i=0; i<format->range_count; i++)
4450     {
4451         INT range_start = max(index, format->character_ranges[i].First);
4452         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
4453         if (range_start < range_end)
4454         {
4455             GpRectF range_rect;
4456             SIZE range_size;
4457
4458             range_rect.Y = bounds->Y;
4459             range_rect.Height = bounds->Height;
4460
4461             GetTextExtentExPointW(hdc, string + index, range_start - index,
4462                                   INT_MAX, NULL, NULL, &range_size);
4463             range_rect.X = bounds->X + range_size.cx;
4464
4465             GetTextExtentExPointW(hdc, string + index, range_end - index,
4466                                   INT_MAX, NULL, NULL, &range_size);
4467             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
4468
4469             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
4470             if (stat != Ok)
4471                 break;
4472         }
4473     }
4474
4475     return stat;
4476 }
4477
4478 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
4479         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
4480         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
4481         INT regionCount, GpRegion** regions)
4482 {
4483     GpStatus stat;
4484     int i;
4485     HFONT oldfont;
4486     struct measure_ranges_args args;
4487     HDC hdc, temp_hdc=NULL;
4488
4489     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
4490             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
4491
4492     if (!(graphics && string && font && layoutRect && stringFormat && regions))
4493         return InvalidParameter;
4494
4495     if (regionCount < stringFormat->range_count)
4496         return InvalidParameter;
4497
4498     if(!graphics->hdc)
4499     {
4500         hdc = temp_hdc = CreateCompatibleDC(0);
4501         if (!temp_hdc) return OutOfMemory;
4502     }
4503     else
4504         hdc = graphics->hdc;
4505
4506     if (stringFormat->attr)
4507         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
4508
4509     oldfont = SelectObject(hdc, CreateFontIndirectW(&font->lfw));
4510
4511     for (i=0; i<stringFormat->range_count; i++)
4512     {
4513         stat = GdipSetEmpty(regions[i]);
4514         if (stat != Ok)
4515             return stat;
4516     }
4517
4518     args.regions = regions;
4519
4520     stat = gdip_format_string(hdc, string, length, font, layoutRect, stringFormat,
4521         measure_ranges_callback, &args);
4522
4523     DeleteObject(SelectObject(hdc, oldfont));
4524
4525     if (temp_hdc)
4526         DeleteDC(temp_hdc);
4527
4528     return stat;
4529 }
4530
4531 struct measure_string_args {
4532     RectF *bounds;
4533     INT *codepointsfitted;
4534     INT *linesfilled;
4535 };
4536
4537 static GpStatus measure_string_callback(HDC hdc,
4538     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4539     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4540     INT lineno, const RectF *bounds, void *user_data)
4541 {
4542     struct measure_string_args *args = user_data;
4543
4544     if (bounds->Width > args->bounds->Width)
4545         args->bounds->Width = bounds->Width;
4546
4547     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
4548         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
4549
4550     if (args->codepointsfitted)
4551         *args->codepointsfitted = index + length;
4552
4553     if (args->linesfilled)
4554         (*args->linesfilled)++;
4555
4556     return Ok;
4557 }
4558
4559 /* Find the smallest rectangle that bounds the text when it is printed in rect
4560  * according to the format options listed in format. If rect has 0 width and
4561  * height, then just find the smallest rectangle that bounds the text when it's
4562  * printed at location (rect->X, rect-Y). */
4563 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
4564     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
4565     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
4566     INT *codepointsfitted, INT *linesfilled)
4567 {
4568     HFONT oldfont;
4569     struct measure_string_args args;
4570     HDC temp_hdc=NULL, hdc;
4571
4572     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
4573         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
4574         bounds, codepointsfitted, linesfilled);
4575
4576     if(!graphics || !string || !font || !rect || !bounds)
4577         return InvalidParameter;
4578
4579     if(!graphics->hdc)
4580     {
4581         hdc = temp_hdc = CreateCompatibleDC(0);
4582         if (!temp_hdc) return OutOfMemory;
4583     }
4584     else
4585         hdc = graphics->hdc;
4586
4587     if(linesfilled) *linesfilled = 0;
4588     if(codepointsfitted) *codepointsfitted = 0;
4589
4590     if(format)
4591         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4592
4593     oldfont = SelectObject(hdc, CreateFontIndirectW(&font->lfw));
4594
4595     bounds->X = rect->X;
4596     bounds->Y = rect->Y;
4597     bounds->Width = 0.0;
4598     bounds->Height = 0.0;
4599
4600     args.bounds = bounds;
4601     args.codepointsfitted = codepointsfitted;
4602     args.linesfilled = linesfilled;
4603
4604     gdip_format_string(hdc, string, length, font, rect, format,
4605         measure_string_callback, &args);
4606
4607     DeleteObject(SelectObject(hdc, oldfont));
4608
4609     if (temp_hdc)
4610         DeleteDC(temp_hdc);
4611
4612     return Ok;
4613 }
4614
4615 struct draw_string_args {
4616     GpGraphics *graphics;
4617     GDIPCONST GpBrush *brush;
4618     REAL x, y, rel_width, rel_height, ascent;
4619 };
4620
4621 static GpStatus draw_string_callback(HDC hdc,
4622     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
4623     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
4624     INT lineno, const RectF *bounds, void *user_data)
4625 {
4626     struct draw_string_args *args = user_data;
4627     PointF position;
4628
4629     position.X = args->x + bounds->X / args->rel_width;
4630     position.Y = args->y + bounds->Y / args->rel_height + args->ascent;
4631
4632     return GdipDrawDriverString(args->graphics, &string[index], length, font,
4633         args->brush, &position,
4634         DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance, NULL);
4635 }
4636
4637 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
4638     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
4639     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
4640 {
4641     HRGN rgn = NULL;
4642     HFONT gdifont;
4643     GpPointF pt[3], rectcpy[4];
4644     POINT corners[4];
4645     REAL rel_width, rel_height;
4646     INT save_state;
4647     REAL offsety = 0.0;
4648     struct draw_string_args args;
4649     RectF scaled_rect;
4650     HDC hdc, temp_hdc=NULL;
4651     TEXTMETRICW textmetric;
4652
4653     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
4654         length, font, debugstr_rectf(rect), format, brush);
4655
4656     if(!graphics || !string || !font || !brush || !rect)
4657         return InvalidParameter;
4658
4659     if(graphics->hdc)
4660     {
4661         hdc = graphics->hdc;
4662     }
4663     else
4664     {
4665         hdc = temp_hdc = CreateCompatibleDC(0);
4666     }
4667
4668     if(format){
4669         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
4670
4671         /* Should be no need to explicitly test for StringAlignmentNear as
4672          * that is default behavior if no alignment is passed. */
4673         if(format->vertalign != StringAlignmentNear){
4674             RectF bounds;
4675             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
4676
4677             if(format->vertalign == StringAlignmentCenter)
4678                 offsety = (rect->Height - bounds.Height) / 2;
4679             else if(format->vertalign == StringAlignmentFar)
4680                 offsety = (rect->Height - bounds.Height);
4681         }
4682     }
4683
4684     save_state = SaveDC(hdc);
4685
4686     pt[0].X = 0.0;
4687     pt[0].Y = 0.0;
4688     pt[1].X = 1.0;
4689     pt[1].Y = 0.0;
4690     pt[2].X = 0.0;
4691     pt[2].Y = 1.0;
4692     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
4693     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
4694                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
4695     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4696                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4697
4698     rectcpy[3].X = rectcpy[0].X = rect->X;
4699     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
4700     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
4701     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
4702     transform_and_round_points(graphics, corners, rectcpy, 4);
4703
4704     scaled_rect.X = 0.0;
4705     scaled_rect.Y = 0.0;
4706     scaled_rect.Width = rel_width * rect->Width;
4707     scaled_rect.Height = rel_height * rect->Height;
4708
4709     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
4710     {
4711         /* FIXME: If only the width or only the height is 0, we should probably still clip */
4712         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
4713         SelectClipRgn(hdc, rgn);
4714     }
4715
4716     get_font_hfont(graphics, font, &gdifont);
4717     SelectObject(hdc, gdifont);
4718
4719     args.graphics = graphics;
4720     args.brush = brush;
4721
4722     args.x = rect->X;
4723     args.y = rect->Y + offsety;
4724
4725     args.rel_width = rel_width;
4726     args.rel_height = rel_height;
4727
4728     GetTextMetricsW(hdc, &textmetric);
4729     args.ascent = textmetric.tmAscent / rel_height;
4730
4731     gdip_format_string(hdc, string, length, font, &scaled_rect, format,
4732         draw_string_callback, &args);
4733
4734     DeleteObject(rgn);
4735     DeleteObject(gdifont);
4736
4737     RestoreDC(hdc, save_state);
4738
4739     DeleteDC(temp_hdc);
4740
4741     return Ok;
4742 }
4743
4744 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
4745 {
4746     TRACE("(%p)\n", graphics);
4747
4748     if(!graphics)
4749         return InvalidParameter;
4750
4751     if(graphics->busy)
4752         return ObjectBusy;
4753
4754     return GdipSetInfinite(graphics->clip);
4755 }
4756
4757 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
4758 {
4759     TRACE("(%p)\n", graphics);
4760
4761     if(!graphics)
4762         return InvalidParameter;
4763
4764     if(graphics->busy)
4765         return ObjectBusy;
4766
4767     graphics->worldtrans->matrix[0] = 1.0;
4768     graphics->worldtrans->matrix[1] = 0.0;
4769     graphics->worldtrans->matrix[2] = 0.0;
4770     graphics->worldtrans->matrix[3] = 1.0;
4771     graphics->worldtrans->matrix[4] = 0.0;
4772     graphics->worldtrans->matrix[5] = 0.0;
4773
4774     return Ok;
4775 }
4776
4777 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
4778 {
4779     return GdipEndContainer(graphics, state);
4780 }
4781
4782 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
4783     GpMatrixOrder order)
4784 {
4785     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
4786
4787     if(!graphics)
4788         return InvalidParameter;
4789
4790     if(graphics->busy)
4791         return ObjectBusy;
4792
4793     return GdipRotateMatrix(graphics->worldtrans, angle, order);
4794 }
4795
4796 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
4797 {
4798     return GdipBeginContainer2(graphics, state);
4799 }
4800
4801 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
4802         GraphicsContainer *state)
4803 {
4804     GraphicsContainerItem *container;
4805     GpStatus sts;
4806
4807     TRACE("(%p, %p)\n", graphics, state);
4808
4809     if(!graphics || !state)
4810         return InvalidParameter;
4811
4812     sts = init_container(&container, graphics);
4813     if(sts != Ok)
4814         return sts;
4815
4816     list_add_head(&graphics->containers, &container->entry);
4817     *state = graphics->contid = container->contid;
4818
4819     return Ok;
4820 }
4821
4822 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
4823 {
4824     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4825     return NotImplemented;
4826 }
4827
4828 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
4829 {
4830     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4831     return NotImplemented;
4832 }
4833
4834 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4835 {
4836     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4837     return NotImplemented;
4838 }
4839
4840 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4841 {
4842     GpStatus sts;
4843     GraphicsContainerItem *container, *container2;
4844
4845     TRACE("(%p, %x)\n", graphics, state);
4846
4847     if(!graphics)
4848         return InvalidParameter;
4849
4850     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4851         if(container->contid == state)
4852             break;
4853     }
4854
4855     /* did not find a matching container */
4856     if(&container->entry == &graphics->containers)
4857         return Ok;
4858
4859     sts = restore_container(graphics, container);
4860     if(sts != Ok)
4861         return sts;
4862
4863     /* remove all of the containers on top of the found container */
4864     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4865         if(container->contid == state)
4866             break;
4867         list_remove(&container->entry);
4868         delete_container(container);
4869     }
4870
4871     list_remove(&container->entry);
4872     delete_container(container);
4873
4874     return Ok;
4875 }
4876
4877 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4878     REAL sy, GpMatrixOrder order)
4879 {
4880     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4881
4882     if(!graphics)
4883         return InvalidParameter;
4884
4885     if(graphics->busy)
4886         return ObjectBusy;
4887
4888     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4889 }
4890
4891 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4892     CombineMode mode)
4893 {
4894     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4895
4896     if(!graphics || !srcgraphics)
4897         return InvalidParameter;
4898
4899     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4900 }
4901
4902 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4903     CompositingMode mode)
4904 {
4905     TRACE("(%p, %d)\n", graphics, mode);
4906
4907     if(!graphics)
4908         return InvalidParameter;
4909
4910     if(graphics->busy)
4911         return ObjectBusy;
4912
4913     graphics->compmode = mode;
4914
4915     return Ok;
4916 }
4917
4918 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4919     CompositingQuality quality)
4920 {
4921     TRACE("(%p, %d)\n", graphics, quality);
4922
4923     if(!graphics)
4924         return InvalidParameter;
4925
4926     if(graphics->busy)
4927         return ObjectBusy;
4928
4929     graphics->compqual = quality;
4930
4931     return Ok;
4932 }
4933
4934 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4935     InterpolationMode mode)
4936 {
4937     TRACE("(%p, %d)\n", graphics, mode);
4938
4939     if(!graphics || mode == InterpolationModeInvalid || mode > InterpolationModeHighQualityBicubic)
4940         return InvalidParameter;
4941
4942     if(graphics->busy)
4943         return ObjectBusy;
4944
4945     if (mode == InterpolationModeDefault || mode == InterpolationModeLowQuality)
4946         mode = InterpolationModeBilinear;
4947
4948     if (mode == InterpolationModeHighQuality)
4949         mode = InterpolationModeHighQualityBicubic;
4950
4951     graphics->interpolation = mode;
4952
4953     return Ok;
4954 }
4955
4956 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4957 {
4958     TRACE("(%p, %.2f)\n", graphics, scale);
4959
4960     if(!graphics || (scale <= 0.0))
4961         return InvalidParameter;
4962
4963     if(graphics->busy)
4964         return ObjectBusy;
4965
4966     graphics->scale = scale;
4967
4968     return Ok;
4969 }
4970
4971 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4972 {
4973     TRACE("(%p, %d)\n", graphics, unit);
4974
4975     if(!graphics)
4976         return InvalidParameter;
4977
4978     if(graphics->busy)
4979         return ObjectBusy;
4980
4981     if(unit == UnitWorld)
4982         return InvalidParameter;
4983
4984     graphics->unit = unit;
4985
4986     return Ok;
4987 }
4988
4989 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4990     mode)
4991 {
4992     TRACE("(%p, %d)\n", graphics, mode);
4993
4994     if(!graphics)
4995         return InvalidParameter;
4996
4997     if(graphics->busy)
4998         return ObjectBusy;
4999
5000     graphics->pixeloffset = mode;
5001
5002     return Ok;
5003 }
5004
5005 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
5006 {
5007     static int calls;
5008
5009     TRACE("(%p,%i,%i)\n", graphics, x, y);
5010
5011     if (!(calls++))
5012         FIXME("not implemented\n");
5013
5014     return NotImplemented;
5015 }
5016
5017 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
5018 {
5019     static int calls;
5020
5021     TRACE("(%p,%p,%p)\n", graphics, x, y);
5022
5023     if (!(calls++))
5024         FIXME("not implemented\n");
5025
5026     *x = *y = 0;
5027
5028     return NotImplemented;
5029 }
5030
5031 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
5032 {
5033     TRACE("(%p, %d)\n", graphics, mode);
5034
5035     if(!graphics)
5036         return InvalidParameter;
5037
5038     if(graphics->busy)
5039         return ObjectBusy;
5040
5041     graphics->smoothing = mode;
5042
5043     return Ok;
5044 }
5045
5046 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
5047 {
5048     TRACE("(%p, %d)\n", graphics, contrast);
5049
5050     if(!graphics)
5051         return InvalidParameter;
5052
5053     graphics->textcontrast = contrast;
5054
5055     return Ok;
5056 }
5057
5058 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
5059     TextRenderingHint hint)
5060 {
5061     TRACE("(%p, %d)\n", graphics, hint);
5062
5063     if(!graphics || hint > TextRenderingHintClearTypeGridFit)
5064         return InvalidParameter;
5065
5066     if(graphics->busy)
5067         return ObjectBusy;
5068
5069     graphics->texthint = hint;
5070
5071     return Ok;
5072 }
5073
5074 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
5075 {
5076     TRACE("(%p, %p)\n", graphics, matrix);
5077
5078     if(!graphics || !matrix)
5079         return InvalidParameter;
5080
5081     if(graphics->busy)
5082         return ObjectBusy;
5083
5084     GdipDeleteMatrix(graphics->worldtrans);
5085     return GdipCloneMatrix(matrix, &graphics->worldtrans);
5086 }
5087
5088 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
5089     REAL dy, GpMatrixOrder order)
5090 {
5091     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
5092
5093     if(!graphics)
5094         return InvalidParameter;
5095
5096     if(graphics->busy)
5097         return ObjectBusy;
5098
5099     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
5100 }
5101
5102 /*****************************************************************************
5103  * GdipSetClipHrgn [GDIPLUS.@]
5104  */
5105 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
5106 {
5107     GpRegion *region;
5108     GpStatus status;
5109
5110     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
5111
5112     if(!graphics)
5113         return InvalidParameter;
5114
5115     status = GdipCreateRegionHrgn(hrgn, &region);
5116     if(status != Ok)
5117         return status;
5118
5119     status = GdipSetClipRegion(graphics, region, mode);
5120
5121     GdipDeleteRegion(region);
5122     return status;
5123 }
5124
5125 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
5126 {
5127     TRACE("(%p, %p, %d)\n", graphics, path, mode);
5128
5129     if(!graphics)
5130         return InvalidParameter;
5131
5132     if(graphics->busy)
5133         return ObjectBusy;
5134
5135     return GdipCombineRegionPath(graphics->clip, path, mode);
5136 }
5137
5138 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
5139                                     REAL width, REAL height,
5140                                     CombineMode mode)
5141 {
5142     GpRectF rect;
5143
5144     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
5145
5146     if(!graphics)
5147         return InvalidParameter;
5148
5149     if(graphics->busy)
5150         return ObjectBusy;
5151
5152     rect.X = x;
5153     rect.Y = y;
5154     rect.Width  = width;
5155     rect.Height = height;
5156
5157     return GdipCombineRegionRect(graphics->clip, &rect, mode);
5158 }
5159
5160 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
5161                                      INT width, INT height,
5162                                      CombineMode mode)
5163 {
5164     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
5165
5166     if(!graphics)
5167         return InvalidParameter;
5168
5169     if(graphics->busy)
5170         return ObjectBusy;
5171
5172     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
5173 }
5174
5175 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
5176                                       CombineMode mode)
5177 {
5178     TRACE("(%p, %p, %d)\n", graphics, region, mode);
5179
5180     if(!graphics || !region)
5181         return InvalidParameter;
5182
5183     if(graphics->busy)
5184         return ObjectBusy;
5185
5186     return GdipCombineRegionRegion(graphics->clip, region, mode);
5187 }
5188
5189 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
5190     UINT limitDpi)
5191 {
5192     static int calls;
5193
5194     TRACE("(%p,%u)\n", metafile, limitDpi);
5195
5196     if(!(calls++))
5197         FIXME("not implemented\n");
5198
5199     return NotImplemented;
5200 }
5201
5202 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
5203     INT count)
5204 {
5205     INT save_state;
5206     POINT *pti;
5207
5208     TRACE("(%p, %p, %d)\n", graphics, points, count);
5209
5210     if(!graphics || !pen || count<=0)
5211         return InvalidParameter;
5212
5213     if(graphics->busy)
5214         return ObjectBusy;
5215
5216     if (!graphics->hdc)
5217     {
5218         FIXME("graphics object has no HDC\n");
5219         return Ok;
5220     }
5221
5222     pti = GdipAlloc(sizeof(POINT) * count);
5223
5224     save_state = prepare_dc(graphics, pen);
5225     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
5226
5227     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
5228     Polygon(graphics->hdc, pti, count);
5229
5230     restore_dc(graphics, save_state);
5231     GdipFree(pti);
5232
5233     return Ok;
5234 }
5235
5236 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
5237     INT count)
5238 {
5239     GpStatus ret;
5240     GpPointF *ptf;
5241     INT i;
5242
5243     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
5244
5245     if(count<=0)    return InvalidParameter;
5246     ptf = GdipAlloc(sizeof(GpPointF) * count);
5247
5248     for(i = 0;i < count; i++){
5249         ptf[i].X = (REAL)points[i].X;
5250         ptf[i].Y = (REAL)points[i].Y;
5251     }
5252
5253     ret = GdipDrawPolygon(graphics,pen,ptf,count);
5254     GdipFree(ptf);
5255
5256     return ret;
5257 }
5258
5259 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
5260 {
5261     TRACE("(%p, %p)\n", graphics, dpi);
5262
5263     if(!graphics || !dpi)
5264         return InvalidParameter;
5265
5266     if(graphics->busy)
5267         return ObjectBusy;
5268
5269     if (graphics->image)
5270         *dpi = graphics->image->xres;
5271     else
5272         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
5273
5274     return Ok;
5275 }
5276
5277 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
5278 {
5279     TRACE("(%p, %p)\n", graphics, dpi);
5280
5281     if(!graphics || !dpi)
5282         return InvalidParameter;
5283
5284     if(graphics->busy)
5285         return ObjectBusy;
5286
5287     if (graphics->image)
5288         *dpi = graphics->image->yres;
5289     else
5290         *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
5291
5292     return Ok;
5293 }
5294
5295 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
5296     GpMatrixOrder order)
5297 {
5298     GpMatrix m;
5299     GpStatus ret;
5300
5301     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
5302
5303     if(!graphics || !matrix)
5304         return InvalidParameter;
5305
5306     if(graphics->busy)
5307         return ObjectBusy;
5308
5309     m = *(graphics->worldtrans);
5310
5311     ret = GdipMultiplyMatrix(&m, matrix, order);
5312     if(ret == Ok)
5313         *(graphics->worldtrans) = m;
5314
5315     return ret;
5316 }
5317
5318 /* Color used to fill bitmaps so we can tell which parts have been drawn over by gdi32. */
5319 static const COLORREF DC_BACKGROUND_KEY = 0x0c0b0d;
5320
5321 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
5322 {
5323     GpStatus stat=Ok;
5324
5325     TRACE("(%p, %p)\n", graphics, hdc);
5326
5327     if(!graphics || !hdc)
5328         return InvalidParameter;
5329
5330     if(graphics->busy)
5331         return ObjectBusy;
5332
5333     if (graphics->image && graphics->image->type == ImageTypeMetafile)
5334     {
5335         stat = METAFILE_GetDC((GpMetafile*)graphics->image, hdc);
5336     }
5337     else if (!graphics->hdc ||
5338         (graphics->image && graphics->image->type == ImageTypeBitmap && ((GpBitmap*)graphics->image)->format & PixelFormatAlpha))
5339     {
5340         /* Create a fake HDC and fill it with a constant color. */
5341         HDC temp_hdc;
5342         HBITMAP hbitmap;
5343         GpRectF bounds;
5344         BITMAPINFOHEADER bmih;
5345         int i;
5346
5347         stat = get_graphics_bounds(graphics, &bounds);
5348         if (stat != Ok)
5349             return stat;
5350
5351         graphics->temp_hbitmap_width = bounds.Width;
5352         graphics->temp_hbitmap_height = bounds.Height;
5353
5354         bmih.biSize = sizeof(bmih);
5355         bmih.biWidth = graphics->temp_hbitmap_width;
5356         bmih.biHeight = -graphics->temp_hbitmap_height;
5357         bmih.biPlanes = 1;
5358         bmih.biBitCount = 32;
5359         bmih.biCompression = BI_RGB;
5360         bmih.biSizeImage = 0;
5361         bmih.biXPelsPerMeter = 0;
5362         bmih.biYPelsPerMeter = 0;
5363         bmih.biClrUsed = 0;
5364         bmih.biClrImportant = 0;
5365
5366         hbitmap = CreateDIBSection(NULL, (BITMAPINFO*)&bmih, DIB_RGB_COLORS,
5367             (void**)&graphics->temp_bits, NULL, 0);
5368         if (!hbitmap)
5369             return GenericError;
5370
5371         temp_hdc = CreateCompatibleDC(0);
5372         if (!temp_hdc)
5373         {
5374             DeleteObject(hbitmap);
5375             return GenericError;
5376         }
5377
5378         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5379             ((DWORD*)graphics->temp_bits)[i] = DC_BACKGROUND_KEY;
5380
5381         SelectObject(temp_hdc, hbitmap);
5382
5383         graphics->temp_hbitmap = hbitmap;
5384         *hdc = graphics->temp_hdc = temp_hdc;
5385     }
5386     else
5387     {
5388         *hdc = graphics->hdc;
5389     }
5390
5391     if (stat == Ok)
5392         graphics->busy = TRUE;
5393
5394     return stat;
5395 }
5396
5397 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
5398 {
5399     GpStatus stat=Ok;
5400
5401     TRACE("(%p, %p)\n", graphics, hdc);
5402
5403     if(!graphics || !hdc || !graphics->busy)
5404         return InvalidParameter;
5405
5406     if (graphics->image && graphics->image->type == ImageTypeMetafile)
5407     {
5408         stat = METAFILE_ReleaseDC((GpMetafile*)graphics->image, hdc);
5409     }
5410     else if (graphics->temp_hdc == hdc)
5411     {
5412         DWORD* pos;
5413         int i;
5414
5415         /* Find the pixels that have changed, and mark them as opaque. */
5416         pos = (DWORD*)graphics->temp_bits;
5417         for (i=0; i<(graphics->temp_hbitmap_width * graphics->temp_hbitmap_height); i++)
5418         {
5419             if (*pos != DC_BACKGROUND_KEY)
5420             {
5421                 *pos |= 0xff000000;
5422             }
5423             pos++;
5424         }
5425
5426         /* Write the changed pixels to the real target. */
5427         alpha_blend_pixels(graphics, 0, 0, graphics->temp_bits,
5428             graphics->temp_hbitmap_width, graphics->temp_hbitmap_height,
5429             graphics->temp_hbitmap_width * 4);
5430
5431         /* Clean up. */
5432         DeleteDC(graphics->temp_hdc);
5433         DeleteObject(graphics->temp_hbitmap);
5434         graphics->temp_hdc = NULL;
5435         graphics->temp_hbitmap = NULL;
5436     }
5437     else if (hdc != graphics->hdc)
5438     {
5439         stat = InvalidParameter;
5440     }
5441
5442     if (stat == Ok)
5443         graphics->busy = FALSE;
5444
5445     return stat;
5446 }
5447
5448 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
5449 {
5450     GpRegion *clip;
5451     GpStatus status;
5452
5453     TRACE("(%p, %p)\n", graphics, region);
5454
5455     if(!graphics || !region)
5456         return InvalidParameter;
5457
5458     if(graphics->busy)
5459         return ObjectBusy;
5460
5461     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
5462         return status;
5463
5464     /* free everything except root node and header */
5465     delete_element(&region->node);
5466     memcpy(region, clip, sizeof(GpRegion));
5467     GdipFree(clip);
5468
5469     return Ok;
5470 }
5471
5472 static GpStatus get_graphics_transform(GpGraphics *graphics, GpCoordinateSpace dst_space,
5473         GpCoordinateSpace src_space, GpMatrix **matrix)
5474 {
5475     GpStatus stat = GdipCreateMatrix(matrix);
5476     REAL unitscale;
5477
5478     if (dst_space != src_space && stat == Ok)
5479     {
5480         unitscale = convert_unit(graphics_res(graphics), graphics->unit);
5481
5482         if(graphics->unit != UnitDisplay)
5483             unitscale *= graphics->scale;
5484
5485         /* transform from src_space to CoordinateSpacePage */
5486         switch (src_space)
5487         {
5488         case CoordinateSpaceWorld:
5489             GdipMultiplyMatrix(*matrix, graphics->worldtrans, MatrixOrderAppend);
5490             break;
5491         case CoordinateSpacePage:
5492             break;
5493         case CoordinateSpaceDevice:
5494             GdipScaleMatrix(*matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
5495             break;
5496         }
5497
5498         /* transform from CoordinateSpacePage to dst_space */
5499         switch (dst_space)
5500         {
5501         case CoordinateSpaceWorld:
5502             {
5503                 GpMatrix *inverted_transform;
5504                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
5505                 if (stat == Ok)
5506                 {
5507                     stat = GdipInvertMatrix(inverted_transform);
5508                     if (stat == Ok)
5509                         GdipMultiplyMatrix(*matrix, inverted_transform, MatrixOrderAppend);
5510                     GdipDeleteMatrix(inverted_transform);
5511                 }
5512                 break;
5513             }
5514         case CoordinateSpacePage:
5515             break;
5516         case CoordinateSpaceDevice:
5517             GdipScaleMatrix(*matrix, unitscale, unitscale, MatrixOrderAppend);
5518             break;
5519         }
5520     }
5521     return stat;
5522 }
5523
5524 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
5525                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
5526 {
5527     GpMatrix *matrix;
5528     GpStatus stat;
5529
5530     if(!graphics || !points || count <= 0)
5531         return InvalidParameter;
5532
5533     if(graphics->busy)
5534         return ObjectBusy;
5535
5536     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5537
5538     if (src_space == dst_space) return Ok;
5539
5540     stat = get_graphics_transform(graphics, dst_space, src_space, &matrix);
5541
5542     if (stat == Ok)
5543     {
5544         stat = GdipTransformMatrixPoints(matrix, points, count);
5545
5546         GdipDeleteMatrix(matrix);
5547     }
5548
5549     return stat;
5550 }
5551
5552 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
5553                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
5554 {
5555     GpPointF *pointsF;
5556     GpStatus ret;
5557     INT i;
5558
5559     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
5560
5561     if(count <= 0)
5562         return InvalidParameter;
5563
5564     pointsF = GdipAlloc(sizeof(GpPointF) * count);
5565     if(!pointsF)
5566         return OutOfMemory;
5567
5568     for(i = 0; i < count; i++){
5569         pointsF[i].X = (REAL)points[i].X;
5570         pointsF[i].Y = (REAL)points[i].Y;
5571     }
5572
5573     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
5574
5575     if(ret == Ok)
5576         for(i = 0; i < count; i++){
5577             points[i].X = roundr(pointsF[i].X);
5578             points[i].Y = roundr(pointsF[i].Y);
5579         }
5580     GdipFree(pointsF);
5581
5582     return ret;
5583 }
5584
5585 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
5586 {
5587     static int calls;
5588
5589     TRACE("\n");
5590
5591     if (!calls++)
5592       FIXME("stub\n");
5593
5594     return NULL;
5595 }
5596
5597 /*****************************************************************************
5598  * GdipTranslateClip [GDIPLUS.@]
5599  */
5600 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
5601 {
5602     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
5603
5604     if(!graphics)
5605         return InvalidParameter;
5606
5607     if(graphics->busy)
5608         return ObjectBusy;
5609
5610     return GdipTranslateRegion(graphics->clip, dx, dy);
5611 }
5612
5613 /*****************************************************************************
5614  * GdipTranslateClipI [GDIPLUS.@]
5615  */
5616 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
5617 {
5618     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
5619
5620     if(!graphics)
5621         return InvalidParameter;
5622
5623     if(graphics->busy)
5624         return ObjectBusy;
5625
5626     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
5627 }
5628
5629
5630 /*****************************************************************************
5631  * GdipMeasureDriverString [GDIPLUS.@]
5632  */
5633 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5634                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
5635                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
5636 {
5637     static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
5638     HFONT hfont;
5639     HDC hdc;
5640     REAL min_x, min_y, max_x, max_y, x, y;
5641     int i;
5642     TEXTMETRICW textmetric;
5643     const WORD *glyph_indices;
5644     WORD *dynamic_glyph_indices=NULL;
5645     REAL rel_width, rel_height, ascent, descent;
5646     GpPointF pt[3];
5647
5648     TRACE("(%p %p %d %p %p %d %p %p)\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
5649
5650     if (!graphics || !text || !font || !positions || !boundingBox)
5651         return InvalidParameter;
5652
5653     if (length == -1)
5654         length = strlenW(text);
5655
5656     if (length == 0)
5657     {
5658         boundingBox->X = 0.0;
5659         boundingBox->Y = 0.0;
5660         boundingBox->Width = 0.0;
5661         boundingBox->Height = 0.0;
5662     }
5663
5664     if (flags & unsupported_flags)
5665         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
5666
5667     if (matrix)
5668         FIXME("Ignoring matrix\n");
5669
5670     get_font_hfont(graphics, font, &hfont);
5671
5672     hdc = CreateCompatibleDC(0);
5673     SelectObject(hdc, hfont);
5674
5675     GetTextMetricsW(hdc, &textmetric);
5676
5677     pt[0].X = 0.0;
5678     pt[0].Y = 0.0;
5679     pt[1].X = 1.0;
5680     pt[1].Y = 0.0;
5681     pt[2].X = 0.0;
5682     pt[2].Y = 1.0;
5683     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
5684     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
5685                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
5686     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
5687                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
5688
5689     if (flags & DriverStringOptionsCmapLookup)
5690     {
5691         glyph_indices = dynamic_glyph_indices = GdipAlloc(sizeof(WORD) * length);
5692         if (!glyph_indices)
5693         {
5694             DeleteDC(hdc);
5695             DeleteObject(hfont);
5696             return OutOfMemory;
5697         }
5698
5699         GetGlyphIndicesW(hdc, text, length, dynamic_glyph_indices, 0);
5700     }
5701     else
5702         glyph_indices = text;
5703
5704     min_x = max_x = x = positions[0].X;
5705     min_y = max_y = y = positions[0].Y;
5706
5707     ascent = textmetric.tmAscent / rel_height;
5708     descent = textmetric.tmDescent / rel_height;
5709
5710     for (i=0; i<length; i++)
5711     {
5712         int char_width;
5713         ABC abc;
5714
5715         if (!(flags & DriverStringOptionsRealizedAdvance))
5716         {
5717             x = positions[i].X;
5718             y = positions[i].Y;
5719         }
5720
5721         GetCharABCWidthsW(hdc, glyph_indices[i], glyph_indices[i], &abc);
5722         char_width = abc.abcA + abc.abcB + abc.abcB;
5723
5724         if (min_y > y - ascent) min_y = y - ascent;
5725         if (max_y < y + descent) max_y = y + descent;
5726         if (min_x > x) min_x = x;
5727
5728         x += char_width / rel_width;
5729
5730         if (max_x < x) max_x = x;
5731     }
5732
5733     GdipFree(dynamic_glyph_indices);
5734     DeleteDC(hdc);
5735     DeleteObject(hfont);
5736
5737     boundingBox->X = min_x;
5738     boundingBox->Y = min_y;
5739     boundingBox->Width = max_x - min_x;
5740     boundingBox->Height = max_y - min_y;
5741
5742     return Ok;
5743 }
5744
5745 static GpStatus GDI32_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5746                                      GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
5747                                      GDIPCONST PointF *positions, INT flags,
5748                                      GDIPCONST GpMatrix *matrix )
5749 {
5750     static const INT unsupported_flags = ~(DriverStringOptionsRealizedAdvance|DriverStringOptionsCmapLookup);
5751     INT save_state;
5752     GpPointF pt;
5753     HFONT hfont;
5754     UINT eto_flags=0;
5755
5756     if (flags & unsupported_flags)
5757         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
5758
5759     if (matrix)
5760         FIXME("Ignoring matrix\n");
5761
5762     if (!(flags & DriverStringOptionsCmapLookup))
5763         eto_flags |= ETO_GLYPH_INDEX;
5764
5765     save_state = SaveDC(graphics->hdc);
5766     SetBkMode(graphics->hdc, TRANSPARENT);
5767     SetTextColor(graphics->hdc, brush->lb.lbColor);
5768
5769     pt = positions[0];
5770     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, &pt, 1);
5771
5772     get_font_hfont(graphics, font, &hfont);
5773     SelectObject(graphics->hdc, hfont);
5774
5775     SetTextAlign(graphics->hdc, TA_BASELINE|TA_LEFT);
5776
5777     ExtTextOutW(graphics->hdc, roundr(pt.X), roundr(pt.Y), eto_flags, NULL, text, length, NULL);
5778
5779     RestoreDC(graphics->hdc, save_state);
5780
5781     DeleteObject(hfont);
5782
5783     return Ok;
5784 }
5785
5786 static GpStatus SOFTWARE_GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5787                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
5788                                          GDIPCONST PointF *positions, INT flags,
5789                                          GDIPCONST GpMatrix *matrix )
5790 {
5791     static const INT unsupported_flags = ~(DriverStringOptionsCmapLookup|DriverStringOptionsRealizedAdvance);
5792     GpStatus stat;
5793     PointF *real_positions, real_position;
5794     POINT *pti;
5795     HFONT hfont;
5796     HDC hdc;
5797     int min_x=INT_MAX, min_y=INT_MAX, max_x=INT_MIN, max_y=INT_MIN, i, x, y;
5798     DWORD max_glyphsize=0;
5799     GLYPHMETRICS glyphmetrics;
5800     static const MAT2 identity = {{0,1}, {0,0}, {0,0}, {0,1}};
5801     BYTE *glyph_mask;
5802     BYTE *text_mask;
5803     int text_mask_stride;
5804     BYTE *pixel_data;
5805     int pixel_data_stride;
5806     GpRect pixel_area;
5807     UINT ggo_flags = GGO_GRAY8_BITMAP;
5808
5809     if (length <= 0)
5810         return Ok;
5811
5812     if (!(flags & DriverStringOptionsCmapLookup))
5813         ggo_flags |= GGO_GLYPH_INDEX;
5814
5815     if (flags & unsupported_flags)
5816         FIXME("Ignoring flags %x\n", flags & unsupported_flags);
5817
5818     if (matrix)
5819         FIXME("Ignoring matrix\n");
5820
5821     pti = GdipAlloc(sizeof(POINT) * length);
5822     if (!pti)
5823         return OutOfMemory;
5824
5825     if (flags & DriverStringOptionsRealizedAdvance)
5826     {
5827         real_position = positions[0];
5828
5829         transform_and_round_points(graphics, pti, &real_position, 1);
5830     }
5831     else
5832     {
5833         real_positions = GdipAlloc(sizeof(PointF) * length);
5834         if (!real_positions)
5835         {
5836             GdipFree(pti);
5837             return OutOfMemory;
5838         }
5839
5840         memcpy(real_positions, positions, sizeof(PointF) * length);
5841
5842         transform_and_round_points(graphics, pti, real_positions, length);
5843
5844         GdipFree(real_positions);
5845     }
5846
5847     get_font_hfont(graphics, font, &hfont);
5848
5849     hdc = CreateCompatibleDC(0);
5850     SelectObject(hdc, hfont);
5851
5852     /* Get the boundaries of the text to be drawn */
5853     for (i=0; i<length; i++)
5854     {
5855         DWORD glyphsize;
5856         int left, top, right, bottom;
5857
5858         glyphsize = GetGlyphOutlineW(hdc, text[i], ggo_flags,
5859             &glyphmetrics, 0, NULL, &identity);
5860
5861         if (glyphsize == GDI_ERROR)
5862         {
5863             ERR("GetGlyphOutlineW failed\n");
5864             GdipFree(pti);
5865             DeleteDC(hdc);
5866             DeleteObject(hfont);
5867             return GenericError;
5868         }
5869
5870         if (glyphsize > max_glyphsize)
5871             max_glyphsize = glyphsize;
5872
5873         left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
5874         top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
5875         right = pti[i].x + glyphmetrics.gmptGlyphOrigin.x + glyphmetrics.gmBlackBoxX;
5876         bottom = pti[i].y - glyphmetrics.gmptGlyphOrigin.y + glyphmetrics.gmBlackBoxY;
5877
5878         if (left < min_x) min_x = left;
5879         if (top < min_y) min_y = top;
5880         if (right > max_x) max_x = right;
5881         if (bottom > max_y) max_y = bottom;
5882
5883         if (i+1 < length && (flags & DriverStringOptionsRealizedAdvance) == DriverStringOptionsRealizedAdvance)
5884         {
5885             pti[i+1].x = pti[i].x + glyphmetrics.gmCellIncX;
5886             pti[i+1].y = pti[i].y + glyphmetrics.gmCellIncY;
5887         }
5888     }
5889
5890     glyph_mask = GdipAlloc(max_glyphsize);
5891     text_mask = GdipAlloc((max_x - min_x) * (max_y - min_y));
5892     text_mask_stride = max_x - min_x;
5893
5894     if (!(glyph_mask && text_mask))
5895     {
5896         GdipFree(glyph_mask);
5897         GdipFree(text_mask);
5898         GdipFree(pti);
5899         DeleteDC(hdc);
5900         DeleteObject(hfont);
5901         return OutOfMemory;
5902     }
5903
5904     /* Generate a mask for the text */
5905     for (i=0; i<length; i++)
5906     {
5907         int left, top, stride;
5908
5909         GetGlyphOutlineW(hdc, text[i], ggo_flags,
5910             &glyphmetrics, max_glyphsize, glyph_mask, &identity);
5911
5912         left = pti[i].x + glyphmetrics.gmptGlyphOrigin.x;
5913         top = pti[i].y - glyphmetrics.gmptGlyphOrigin.y;
5914         stride = (glyphmetrics.gmBlackBoxX + 3) & (~3);
5915
5916         for (y=0; y<glyphmetrics.gmBlackBoxY; y++)
5917         {
5918             BYTE *glyph_val = glyph_mask + y * stride;
5919             BYTE *text_val = text_mask + (left - min_x) + (top - min_y + y) * text_mask_stride;
5920             for (x=0; x<glyphmetrics.gmBlackBoxX; x++)
5921             {
5922                 *text_val = min(64, *text_val + *glyph_val);
5923                 glyph_val++;
5924                 text_val++;
5925             }
5926         }
5927     }
5928
5929     GdipFree(pti);
5930     DeleteDC(hdc);
5931     DeleteObject(hfont);
5932     GdipFree(glyph_mask);
5933
5934     /* get the brush data */
5935     pixel_data = GdipAlloc(4 * (max_x - min_x) * (max_y - min_y));
5936     if (!pixel_data)
5937     {
5938         GdipFree(text_mask);
5939         return OutOfMemory;
5940     }
5941
5942     pixel_area.X = min_x;
5943     pixel_area.Y = min_y;
5944     pixel_area.Width = max_x - min_x;
5945     pixel_area.Height = max_y - min_y;
5946     pixel_data_stride = pixel_area.Width * 4;
5947
5948     stat = brush_fill_pixels(graphics, (GpBrush*)brush, (DWORD*)pixel_data, &pixel_area, pixel_area.Width);
5949     if (stat != Ok)
5950     {
5951         GdipFree(text_mask);
5952         GdipFree(pixel_data);
5953         return stat;
5954     }
5955
5956     /* multiply the brush data by the mask */
5957     for (y=0; y<pixel_area.Height; y++)
5958     {
5959         BYTE *text_val = text_mask + text_mask_stride * y;
5960         BYTE *pixel_val = pixel_data + pixel_data_stride * y + 3;
5961         for (x=0; x<pixel_area.Width; x++)
5962         {
5963             *pixel_val = (*pixel_val) * (*text_val) / 64;
5964             text_val++;
5965             pixel_val+=4;
5966         }
5967     }
5968
5969     GdipFree(text_mask);
5970
5971     /* draw the result */
5972     stat = alpha_blend_pixels(graphics, min_x, min_y, pixel_data, pixel_area.Width,
5973         pixel_area.Height, pixel_data_stride);
5974
5975     GdipFree(pixel_data);
5976
5977     return stat;
5978 }
5979
5980 /*****************************************************************************
5981  * GdipDrawDriverString [GDIPLUS.@]
5982  */
5983 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
5984                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
5985                                          GDIPCONST PointF *positions, INT flags,
5986                                          GDIPCONST GpMatrix *matrix )
5987 {
5988     GpStatus stat=NotImplemented;
5989
5990     TRACE("(%p %s %p %p %p %d %p)\n", graphics, debugstr_wn(text, length), font, brush, positions, flags, matrix);
5991
5992     if (!graphics || !text || !font || !brush || !positions)
5993         return InvalidParameter;
5994
5995     if (length == -1)
5996         length = strlenW(text);
5997
5998     if (graphics->hdc &&
5999         ((flags & DriverStringOptionsRealizedAdvance) || length <= 1) &&
6000         brush->bt == BrushTypeSolidColor &&
6001         (((GpSolidFill*)brush)->color & 0xff000000) == 0xff000000)
6002         stat = GDI32_GdipDrawDriverString(graphics, text, length, font, brush,
6003             positions, flags, matrix);
6004
6005     if (stat == NotImplemented)
6006         stat = SOFTWARE_GdipDrawDriverString(graphics, text, length, font, brush,
6007             positions, flags, matrix);
6008
6009     return stat;
6010 }
6011
6012 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
6013                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
6014 {
6015     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
6016     return NotImplemented;
6017 }
6018
6019 /*****************************************************************************
6020  * GdipIsVisibleClipEmpty [GDIPLUS.@]
6021  */
6022 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
6023 {
6024     GpStatus stat;
6025     GpRegion* rgn;
6026
6027     TRACE("(%p, %p)\n", graphics, res);
6028
6029     if((stat = GdipCreateRegion(&rgn)) != Ok)
6030         return stat;
6031
6032     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
6033         goto cleanup;
6034
6035     stat = GdipIsEmptyRegion(rgn, graphics, res);
6036
6037 cleanup:
6038     GdipDeleteRegion(rgn);
6039     return stat;
6040 }