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