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