gdiplus: Give a nice warning when we try to draw to an HDC-less graphics.
[wine] / dlls / gdiplus / graphics.c
1 /*
2  * Copyright (C) 2007 Google (Evan Stade)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20 #include <math.h>
21 #include <limits.h>
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "wine/unicode.h"
28
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
34
35 #include "winreg.h"
36 #include "shlwapi.h"
37
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
41 #include "wine/list.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
44
45 /* looks-right constants */
46 #define ANCHOR_WIDTH (2.0)
47 #define MAX_ITERS (50)
48
49 /* Converts angle (in degrees) to x/y coordinates */
50 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
51 {
52     REAL radAngle, hypotenuse;
53
54     radAngle = deg2rad(angle);
55     hypotenuse = 50.0; /* arbitrary */
56
57     *x = x_0 + cos(radAngle) * hypotenuse;
58     *y = y_0 + sin(radAngle) * hypotenuse;
59 }
60
61 /* Converts from gdiplus path point type to gdi path point type. */
62 static BYTE convert_path_point_type(BYTE type)
63 {
64     BYTE ret;
65
66     switch(type & PathPointTypePathTypeMask){
67         case PathPointTypeBezier:
68             ret = PT_BEZIERTO;
69             break;
70         case PathPointTypeLine:
71             ret = PT_LINETO;
72             break;
73         case PathPointTypeStart:
74             ret = PT_MOVETO;
75             break;
76         default:
77             ERR("Bad point type\n");
78             return 0;
79     }
80
81     if(type & PathPointTypeCloseSubpath)
82         ret |= PT_CLOSEFIGURE;
83
84     return ret;
85 }
86
87 static REAL graphics_res(GpGraphics *graphics)
88 {
89     if (graphics->image) return graphics->image->xres;
90     else return (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
91 }
92
93 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
94 {
95     HPEN gdipen;
96     REAL width;
97     INT save_state, i, numdashes;
98     GpPointF pt[2];
99     DWORD dash_array[MAX_DASHLEN];
100
101     if (!graphics->hdc)
102     {
103         FIXME("graphics object has no HDC\n");
104     }
105
106     save_state = SaveDC(graphics->hdc);
107
108     EndPath(graphics->hdc);
109
110     if(pen->unit == UnitPixel){
111         width = pen->width;
112     }
113     else{
114         /* Get an estimate for the amount the pen width is affected by the world
115          * transform. (This is similar to what some of the wine drivers do.) */
116         pt[0].X = 0.0;
117         pt[0].Y = 0.0;
118         pt[1].X = 1.0;
119         pt[1].Y = 1.0;
120         GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
121         width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
122                      (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
123
124         width *= pen->width * convert_unit(graphics_res(graphics),
125                               pen->unit == UnitWorld ? graphics->unit : pen->unit);
126     }
127
128     if(pen->dash == DashStyleCustom){
129         numdashes = min(pen->numdashes, MAX_DASHLEN);
130
131         TRACE("dashes are: ");
132         for(i = 0; i < numdashes; i++){
133             dash_array[i] = roundr(width * pen->dashes[i]);
134             TRACE("%d, ", dash_array[i]);
135         }
136         TRACE("\n and the pen style is %x\n", pen->style);
137
138         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
139                               numdashes, dash_array);
140     }
141     else
142         gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
143
144     SelectObject(graphics->hdc, gdipen);
145
146     return save_state;
147 }
148
149 static void restore_dc(GpGraphics *graphics, INT state)
150 {
151     DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
152     RestoreDC(graphics->hdc, state);
153 }
154
155 /* This helper applies all the changes that the points listed in ptf need in
156  * order to be drawn on the device context.  In the end, this should include at
157  * least:
158  *  -scaling by page unit
159  *  -applying world transformation
160  *  -converting from float to int
161  * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
162  * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
163  * gdi to draw, and these functions would irreparably mess with line widths.
164  */
165 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
166     GpPointF *ptf, INT count)
167 {
168     REAL unitscale;
169     GpMatrix *matrix;
170     int i;
171
172     unitscale = convert_unit(graphics_res(graphics), graphics->unit);
173
174     /* apply page scale */
175     if(graphics->unit != UnitDisplay)
176         unitscale *= graphics->scale;
177
178     GdipCloneMatrix(graphics->worldtrans, &matrix);
179     GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
180     GdipTransformMatrixPoints(matrix, ptf, count);
181     GdipDeleteMatrix(matrix);
182
183     for(i = 0; i < count; i++){
184         pti[i].x = roundr(ptf[i].X);
185         pti[i].y = roundr(ptf[i].Y);
186     }
187 }
188
189 /* Draw non-premultiplied ARGB data to the given graphics object */
190 static GpStatus alpha_blend_pixels(GpGraphics *graphics, INT dst_x, INT dst_y,
191     const BYTE *src, INT src_width, INT src_height, INT src_stride)
192 {
193     if (graphics->image && graphics->image->type == ImageTypeBitmap)
194     {
195         GpBitmap *dst_bitmap = (GpBitmap*)graphics->image;
196         INT x, y;
197
198         for (x=0; x<src_width; x++)
199         {
200             for (y=0; y<src_height; y++)
201             {
202                 ARGB dst_color, src_color;
203                 GdipBitmapGetPixel(dst_bitmap, x+dst_x, y+dst_y, &dst_color);
204                 src_color = ((ARGB*)(src + src_stride * y))[x];
205                 GdipBitmapSetPixel(dst_bitmap, x+dst_x, y+dst_y, color_over(dst_color, src_color));
206             }
207         }
208
209         return Ok;
210     }
211     else
212     {
213         HDC hdc;
214         HBITMAP hbitmap, old_hbm=NULL;
215         BITMAPINFOHEADER bih;
216         BYTE *temp_bits;
217         BLENDFUNCTION bf;
218
219         hdc = CreateCompatibleDC(0);
220
221         bih.biSize = sizeof(BITMAPINFOHEADER);
222         bih.biWidth = src_width;
223         bih.biHeight = -src_height;
224         bih.biPlanes = 1;
225         bih.biBitCount = 32;
226         bih.biCompression = BI_RGB;
227         bih.biSizeImage = 0;
228         bih.biXPelsPerMeter = 0;
229         bih.biYPelsPerMeter = 0;
230         bih.biClrUsed = 0;
231         bih.biClrImportant = 0;
232
233         hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
234             (void**)&temp_bits, NULL, 0);
235
236         convert_32bppARGB_to_32bppPARGB(src_width, src_height, temp_bits,
237             4 * src_width, src, src_stride);
238
239         old_hbm = SelectObject(hdc, hbitmap);
240
241         bf.BlendOp = AC_SRC_OVER;
242         bf.BlendFlags = 0;
243         bf.SourceConstantAlpha = 255;
244         bf.AlphaFormat = AC_SRC_ALPHA;
245
246         GdiAlphaBlend(graphics->hdc, dst_x, dst_y, src_width, src_height,
247             hdc, 0, 0, src_width, src_height, bf);
248
249         SelectObject(hdc, old_hbm);
250         DeleteDC(hdc);
251         DeleteObject(hbitmap);
252
253         return Ok;
254     }
255 }
256
257 static ARGB blend_colors(ARGB start, ARGB end, REAL position)
258 {
259     ARGB result=0;
260     ARGB i;
261     for (i=0xff; i<=0xff0000; i = i << 8)
262         result |= (int)((start&i)*(1.0f - position)+(end&i)*(position))&i;
263     return result;
264 }
265
266 static ARGB blend_line_gradient(GpLineGradient* brush, REAL position)
267 {
268     REAL blendfac;
269
270     /* clamp to between 0.0 and 1.0, using the wrap mode */
271     if (brush->wrap == WrapModeTile)
272     {
273         position = fmodf(position, 1.0f);
274         if (position < 0.0f) position += 1.0f;
275     }
276     else /* WrapModeFlip* */
277     {
278         position = fmodf(position, 2.0f);
279         if (position < 0.0f) position += 2.0f;
280         if (position > 1.0f) position = 2.0f - position;
281     }
282
283     if (brush->blendcount == 1)
284         blendfac = position;
285     else
286     {
287         int i=1;
288         REAL left_blendpos, left_blendfac, right_blendpos, right_blendfac;
289         REAL range;
290
291         /* locate the blend positions surrounding this position */
292         while (position > brush->blendpos[i])
293             i++;
294
295         /* interpolate between the blend positions */
296         left_blendpos = brush->blendpos[i-1];
297         left_blendfac = brush->blendfac[i-1];
298         right_blendpos = brush->blendpos[i];
299         right_blendfac = brush->blendfac[i];
300         range = right_blendpos - left_blendpos;
301         blendfac = (left_blendfac * (right_blendpos - position) +
302                     right_blendfac * (position - left_blendpos)) / range;
303     }
304
305     if (brush->pblendcount == 0)
306         return blend_colors(brush->startcolor, brush->endcolor, blendfac);
307     else
308     {
309         int i=1;
310         ARGB left_blendcolor, right_blendcolor;
311         REAL left_blendpos, right_blendpos;
312
313         /* locate the blend colors surrounding this position */
314         while (blendfac > brush->pblendpos[i])
315             i++;
316
317         /* interpolate between the blend colors */
318         left_blendpos = brush->pblendpos[i-1];
319         left_blendcolor = brush->pblendcolor[i-1];
320         right_blendpos = brush->pblendpos[i];
321         right_blendcolor = brush->pblendcolor[i];
322         blendfac = (blendfac - left_blendpos) / (right_blendpos - left_blendpos);
323         return blend_colors(left_blendcolor, right_blendcolor, blendfac);
324     }
325 }
326
327 static void brush_fill_path(GpGraphics *graphics, GpBrush* brush)
328 {
329     switch (brush->bt)
330     {
331     case BrushTypeLinearGradient:
332     {
333         GpLineGradient *line = (GpLineGradient*)brush;
334         RECT rc;
335
336         SelectClipPath(graphics->hdc, RGN_AND);
337         if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
338         {
339             GpPointF endpointsf[2];
340             POINT endpointsi[2];
341             POINT poly[4];
342
343             SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
344
345             endpointsf[0] = line->startpoint;
346             endpointsf[1] = line->endpoint;
347             transform_and_round_points(graphics, endpointsi, endpointsf, 2);
348
349             if (abs(endpointsi[0].x-endpointsi[1].x) > abs(endpointsi[0].y-endpointsi[1].y))
350             {
351                 /* vertical-ish gradient */
352                 int startx, endx; /* x co-ordinates of endpoints shifted to intersect the top of the visible rectangle */
353                 int startbottomx; /* x co-ordinate of start point shifted to intersect the bottom of the visible rectangle */
354                 int width;
355                 COLORREF col;
356                 HBRUSH hbrush, hprevbrush;
357                 int leftx, rightx; /* x co-ordinates where the leftmost and rightmost gradient lines hit the top of the visible rectangle */
358                 int x;
359                 int tilt; /* horizontal distance covered by a gradient line */
360
361                 startx = roundr((rc.top - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
362                 endx = roundr((rc.top - endpointsf[1].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[1].X);
363                 width = endx - startx;
364                 startbottomx = roundr((rc.bottom - endpointsf[0].Y) * (endpointsf[1].Y - endpointsf[0].Y) / (endpointsf[0].X - endpointsf[1].X) + endpointsf[0].X);
365                 tilt = startx - startbottomx;
366
367                 if (startx >= startbottomx)
368                 {
369                     leftx = rc.left;
370                     rightx = rc.right + tilt;
371                 }
372                 else
373                 {
374                     leftx = rc.left + tilt;
375                     rightx = rc.right;
376                 }
377
378                 poly[0].y = rc.bottom;
379                 poly[1].y = rc.top;
380                 poly[2].y = rc.top;
381                 poly[3].y = rc.bottom;
382
383                 for (x=leftx; x<=rightx; x++)
384                 {
385                     ARGB argb = blend_line_gradient(line, (x-startx)/(REAL)width);
386                     col = ARGB2COLORREF(argb);
387                     hbrush = CreateSolidBrush(col);
388                     hprevbrush = SelectObject(graphics->hdc, hbrush);
389                     poly[0].x = x - tilt - 1;
390                     poly[1].x = x - 1;
391                     poly[2].x = x;
392                     poly[3].x = x - tilt;
393                     Polygon(graphics->hdc, poly, 4);
394                     SelectObject(graphics->hdc, hprevbrush);
395                     DeleteObject(hbrush);
396                 }
397             }
398             else if (endpointsi[0].y != endpointsi[1].y)
399             {
400                 /* horizontal-ish gradient */
401                 int starty, endy; /* y co-ordinates of endpoints shifted to intersect the left of the visible rectangle */
402                 int startrighty; /* y co-ordinate of start point shifted to intersect the right of the visible rectangle */
403                 int height;
404                 COLORREF col;
405                 HBRUSH hbrush, hprevbrush;
406                 int topy, bottomy; /* y co-ordinates where the topmost and bottommost gradient lines hit the left of the visible rectangle */
407                 int y;
408                 int tilt; /* vertical distance covered by a gradient line */
409
410                 starty = roundr((rc.left - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
411                 endy = roundr((rc.left - endpointsf[1].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[1].Y);
412                 height = endy - starty;
413                 startrighty = roundr((rc.right - endpointsf[0].X) * (endpointsf[0].X - endpointsf[1].X) / (endpointsf[1].Y - endpointsf[0].Y) + endpointsf[0].Y);
414                 tilt = starty - startrighty;
415
416                 if (starty >= startrighty)
417                 {
418                     topy = rc.top;
419                     bottomy = rc.bottom + tilt;
420                 }
421                 else
422                 {
423                     topy = rc.top + tilt;
424                     bottomy = rc.bottom;
425                 }
426
427                 poly[0].x = rc.right;
428                 poly[1].x = rc.left;
429                 poly[2].x = rc.left;
430                 poly[3].x = rc.right;
431
432                 for (y=topy; y<=bottomy; y++)
433                 {
434                     ARGB argb = blend_line_gradient(line, (y-starty)/(REAL)height);
435                     col = ARGB2COLORREF(argb);
436                     hbrush = CreateSolidBrush(col);
437                     hprevbrush = SelectObject(graphics->hdc, hbrush);
438                     poly[0].y = y - tilt - 1;
439                     poly[1].y = y - 1;
440                     poly[2].y = y;
441                     poly[3].y = y - tilt;
442                     Polygon(graphics->hdc, poly, 4);
443                     SelectObject(graphics->hdc, hprevbrush);
444                     DeleteObject(hbrush);
445                 }
446             }
447             /* else startpoint == endpoint */
448         }
449         break;
450     }
451     case BrushTypeSolidColor:
452     {
453         GpSolidFill *fill = (GpSolidFill*)brush;
454         if (fill->bmp)
455         {
456             RECT rc;
457             /* partially transparent fill */
458
459             SelectClipPath(graphics->hdc, RGN_AND);
460             if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
461             {
462                 HDC hdc = CreateCompatibleDC(NULL);
463                 HBITMAP oldbmp;
464                 BLENDFUNCTION bf;
465
466                 if (!hdc) break;
467
468                 oldbmp = SelectObject(hdc, fill->bmp);
469
470                 bf.BlendOp = AC_SRC_OVER;
471                 bf.BlendFlags = 0;
472                 bf.SourceConstantAlpha = 255;
473                 bf.AlphaFormat = AC_SRC_ALPHA;
474
475                 GdiAlphaBlend(graphics->hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, hdc, 0, 0, 1, 1, bf);
476
477                 SelectObject(hdc, oldbmp);
478                 DeleteDC(hdc);
479             }
480
481             break;
482         }
483         /* else fall through */
484     }
485     default:
486         SelectObject(graphics->hdc, brush->gdibrush);
487         FillPath(graphics->hdc);
488         break;
489     }
490 }
491
492 /* GdipDrawPie/GdipFillPie helper function */
493 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
494     REAL height, REAL startAngle, REAL sweepAngle)
495 {
496     GpPointF ptf[4];
497     POINT pti[4];
498
499     ptf[0].X = x;
500     ptf[0].Y = y;
501     ptf[1].X = x + width;
502     ptf[1].Y = y + height;
503
504     deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
505     deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
506
507     transform_and_round_points(graphics, pti, ptf, 4);
508
509     Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
510         pti[2].y, pti[3].x, pti[3].y);
511 }
512
513 /* Draws the linecap the specified color and size on the hdc.  The linecap is in
514  * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
515  * should not be called on an hdc that has a path you care about. */
516 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
517     const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
518 {
519     HGDIOBJ oldbrush = NULL, oldpen = NULL;
520     GpMatrix *matrix = NULL;
521     HBRUSH brush = NULL;
522     HPEN pen = NULL;
523     PointF ptf[4], *custptf = NULL;
524     POINT pt[4], *custpt = NULL;
525     BYTE *tp = NULL;
526     REAL theta, dsmall, dbig, dx, dy = 0.0;
527     INT i, count;
528     LOGBRUSH lb;
529     BOOL customstroke;
530
531     if((x1 == x2) && (y1 == y2))
532         return;
533
534     theta = gdiplus_atan2(y2 - y1, x2 - x1);
535
536     customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
537     if(!customstroke){
538         brush = CreateSolidBrush(color);
539         lb.lbStyle = BS_SOLID;
540         lb.lbColor = color;
541         lb.lbHatch = 0;
542         pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
543                            PS_JOIN_MITER, 1, &lb, 0,
544                            NULL);
545         oldbrush = SelectObject(graphics->hdc, brush);
546         oldpen = SelectObject(graphics->hdc, pen);
547     }
548
549     switch(cap){
550         case LineCapFlat:
551             break;
552         case LineCapSquare:
553         case LineCapSquareAnchor:
554         case LineCapDiamondAnchor:
555             size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
556             if(cap == LineCapDiamondAnchor){
557                 dsmall = cos(theta + M_PI_2) * size;
558                 dbig = sin(theta + M_PI_2) * size;
559             }
560             else{
561                 dsmall = cos(theta + M_PI_4) * size;
562                 dbig = sin(theta + M_PI_4) * size;
563             }
564
565             ptf[0].X = x2 - dsmall;
566             ptf[1].X = x2 + dbig;
567
568             ptf[0].Y = y2 - dbig;
569             ptf[3].Y = y2 + dsmall;
570
571             ptf[1].Y = y2 - dsmall;
572             ptf[2].Y = y2 + dbig;
573
574             ptf[3].X = x2 - dbig;
575             ptf[2].X = x2 + dsmall;
576
577             transform_and_round_points(graphics, pt, ptf, 4);
578             Polygon(graphics->hdc, pt, 4);
579
580             break;
581         case LineCapArrowAnchor:
582             size = size * 4.0 / sqrt(3.0);
583
584             dx = cos(M_PI / 6.0 + theta) * size;
585             dy = sin(M_PI / 6.0 + theta) * size;
586
587             ptf[0].X = x2 - dx;
588             ptf[0].Y = y2 - dy;
589
590             dx = cos(- M_PI / 6.0 + theta) * size;
591             dy = sin(- M_PI / 6.0 + theta) * size;
592
593             ptf[1].X = x2 - dx;
594             ptf[1].Y = y2 - dy;
595
596             ptf[2].X = x2;
597             ptf[2].Y = y2;
598
599             transform_and_round_points(graphics, pt, ptf, 3);
600             Polygon(graphics->hdc, pt, 3);
601
602             break;
603         case LineCapRoundAnchor:
604             dx = dy = ANCHOR_WIDTH * size / 2.0;
605
606             ptf[0].X = x2 - dx;
607             ptf[0].Y = y2 - dy;
608             ptf[1].X = x2 + dx;
609             ptf[1].Y = y2 + dy;
610
611             transform_and_round_points(graphics, pt, ptf, 2);
612             Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
613
614             break;
615         case LineCapTriangle:
616             size = size / 2.0;
617             dx = cos(M_PI_2 + theta) * size;
618             dy = sin(M_PI_2 + theta) * size;
619
620             ptf[0].X = x2 - dx;
621             ptf[0].Y = y2 - dy;
622             ptf[1].X = x2 + dx;
623             ptf[1].Y = y2 + dy;
624
625             dx = cos(theta) * size;
626             dy = sin(theta) * size;
627
628             ptf[2].X = x2 + dx;
629             ptf[2].Y = y2 + dy;
630
631             transform_and_round_points(graphics, pt, ptf, 3);
632             Polygon(graphics->hdc, pt, 3);
633
634             break;
635         case LineCapRound:
636             dx = dy = size / 2.0;
637
638             ptf[0].X = x2 - dx;
639             ptf[0].Y = y2 - dy;
640             ptf[1].X = x2 + dx;
641             ptf[1].Y = y2 + dy;
642
643             dx = -cos(M_PI_2 + theta) * size;
644             dy = -sin(M_PI_2 + theta) * size;
645
646             ptf[2].X = x2 - dx;
647             ptf[2].Y = y2 - dy;
648             ptf[3].X = x2 + dx;
649             ptf[3].Y = y2 + dy;
650
651             transform_and_round_points(graphics, pt, ptf, 4);
652             Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
653                 pt[2].y, pt[3].x, pt[3].y);
654
655             break;
656         case LineCapCustom:
657             if(!custom)
658                 break;
659
660             count = custom->pathdata.Count;
661             custptf = GdipAlloc(count * sizeof(PointF));
662             custpt = GdipAlloc(count * sizeof(POINT));
663             tp = GdipAlloc(count);
664
665             if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
666                 goto custend;
667
668             memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
669
670             GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
671             GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
672                              MatrixOrderAppend);
673             GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
674             GdipTransformMatrixPoints(matrix, custptf, count);
675
676             transform_and_round_points(graphics, custpt, custptf, count);
677
678             for(i = 0; i < count; i++)
679                 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
680
681             if(custom->fill){
682                 BeginPath(graphics->hdc);
683                 PolyDraw(graphics->hdc, custpt, tp, count);
684                 EndPath(graphics->hdc);
685                 StrokeAndFillPath(graphics->hdc);
686             }
687             else
688                 PolyDraw(graphics->hdc, custpt, tp, count);
689
690 custend:
691             GdipFree(custptf);
692             GdipFree(custpt);
693             GdipFree(tp);
694             GdipDeleteMatrix(matrix);
695             break;
696         default:
697             break;
698     }
699
700     if(!customstroke){
701         SelectObject(graphics->hdc, oldbrush);
702         SelectObject(graphics->hdc, oldpen);
703         DeleteObject(brush);
704         DeleteObject(pen);
705     }
706 }
707
708 /* Shortens the line by the given percent by changing x2, y2.
709  * If percent is > 1.0 then the line will change direction.
710  * If percent is negative it can lengthen the line. */
711 static void shorten_line_percent(REAL x1, REAL  y1, REAL *x2, REAL *y2, REAL percent)
712 {
713     REAL dist, theta, dx, dy;
714
715     if((y1 == *y2) && (x1 == *x2))
716         return;
717
718     dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
719     theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
720     dx = cos(theta) * dist;
721     dy = sin(theta) * dist;
722
723     *x2 = *x2 + dx;
724     *y2 = *y2 + dy;
725 }
726
727 /* Shortens the line by the given amount by changing x2, y2.
728  * If the amount is greater than the distance, the line will become length 0.
729  * If the amount is negative, it can lengthen the line. */
730 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
731 {
732     REAL dx, dy, percent;
733
734     dx = *x2 - x1;
735     dy = *y2 - y1;
736     if(dx == 0 && dy == 0)
737         return;
738
739     percent = amt / sqrt(dx * dx + dy * dy);
740     if(percent >= 1.0){
741         *x2 = x1;
742         *y2 = y1;
743         return;
744     }
745
746     shorten_line_percent(x1, y1, x2, y2, percent);
747 }
748
749 /* Draws lines between the given points, and if caps is true then draws an endcap
750  * at the end of the last line. */
751 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
752     GDIPCONST GpPointF * pt, INT count, BOOL caps)
753 {
754     POINT *pti = NULL;
755     GpPointF *ptcopy = NULL;
756     GpStatus status = GenericError;
757
758     if(!count)
759         return Ok;
760
761     pti = GdipAlloc(count * sizeof(POINT));
762     ptcopy = GdipAlloc(count * sizeof(GpPointF));
763
764     if(!pti || !ptcopy){
765         status = OutOfMemory;
766         goto end;
767     }
768
769     memcpy(ptcopy, pt, count * sizeof(GpPointF));
770
771     if(caps){
772         if(pen->endcap == LineCapArrowAnchor)
773             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
774                              &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
775         else if((pen->endcap == LineCapCustom) && pen->customend)
776             shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
777                              &ptcopy[count-1].X, &ptcopy[count-1].Y,
778                              pen->customend->inset * pen->width);
779
780         if(pen->startcap == LineCapArrowAnchor)
781             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
782                              &ptcopy[0].X, &ptcopy[0].Y, pen->width);
783         else if((pen->startcap == LineCapCustom) && pen->customstart)
784             shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
785                              &ptcopy[0].X, &ptcopy[0].Y,
786                              pen->customstart->inset * pen->width);
787
788         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
789                  pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
790         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
791                          pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
792     }
793
794     transform_and_round_points(graphics, pti, ptcopy, count);
795
796     if(Polyline(graphics->hdc, pti, count))
797         status = Ok;
798
799 end:
800     GdipFree(pti);
801     GdipFree(ptcopy);
802
803     return status;
804 }
805
806 /* Conducts a linear search to find the bezier points that will back off
807  * the endpoint of the curve by a distance of amt. Linear search works
808  * better than binary in this case because there are multiple solutions,
809  * and binary searches often find a bad one. I don't think this is what
810  * Windows does but short of rendering the bezier without GDI's help it's
811  * the best we can do. If rev then work from the start of the passed points
812  * instead of the end. */
813 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
814 {
815     GpPointF origpt[4];
816     REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
817     INT i, first = 0, second = 1, third = 2, fourth = 3;
818
819     if(rev){
820         first = 3;
821         second = 2;
822         third = 1;
823         fourth = 0;
824     }
825
826     origx = pt[fourth].X;
827     origy = pt[fourth].Y;
828     memcpy(origpt, pt, sizeof(GpPointF) * 4);
829
830     for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
831         /* reset bezier points to original values */
832         memcpy(pt, origpt, sizeof(GpPointF) * 4);
833         /* Perform magic on bezier points. Order is important here.*/
834         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
835         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
836         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
837         shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
838         shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
839         shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
840
841         dx = pt[fourth].X - origx;
842         dy = pt[fourth].Y - origy;
843
844         diff = sqrt(dx * dx + dy * dy);
845         percent += 0.0005 * amt;
846     }
847 }
848
849 /* Draws bezier curves between given points, and if caps is true then draws an
850  * endcap at the end of the last line. */
851 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
852     GDIPCONST GpPointF * pt, INT count, BOOL caps)
853 {
854     POINT *pti;
855     GpPointF *ptcopy;
856     GpStatus status = GenericError;
857
858     if(!count)
859         return Ok;
860
861     pti = GdipAlloc(count * sizeof(POINT));
862     ptcopy = GdipAlloc(count * sizeof(GpPointF));
863
864     if(!pti || !ptcopy){
865         status = OutOfMemory;
866         goto end;
867     }
868
869     memcpy(ptcopy, pt, count * sizeof(GpPointF));
870
871     if(caps){
872         if(pen->endcap == LineCapArrowAnchor)
873             shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
874         else if((pen->endcap == LineCapCustom) && pen->customend)
875             shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
876                                FALSE);
877
878         if(pen->startcap == LineCapArrowAnchor)
879             shorten_bezier_amt(ptcopy, pen->width, TRUE);
880         else if((pen->startcap == LineCapCustom) && pen->customstart)
881             shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
882
883         /* the direction of the line cap is parallel to the direction at the
884          * end of the bezier (which, if it has been shortened, is not the same
885          * as the direction from pt[count-2] to pt[count-1]) */
886         draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
887             pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
888             pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
889             pt[count - 1].X, pt[count - 1].Y);
890
891         draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
892             pt[0].X - (ptcopy[0].X - ptcopy[1].X),
893             pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
894     }
895
896     transform_and_round_points(graphics, pti, ptcopy, count);
897
898     PolyBezier(graphics->hdc, pti, count);
899
900     status = Ok;
901
902 end:
903     GdipFree(pti);
904     GdipFree(ptcopy);
905
906     return status;
907 }
908
909 /* Draws a combination of bezier curves and lines between points. */
910 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
911     GDIPCONST BYTE * types, INT count, BOOL caps)
912 {
913     POINT *pti = GdipAlloc(count * sizeof(POINT));
914     BYTE *tp = GdipAlloc(count);
915     GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
916     INT i, j;
917     GpStatus status = GenericError;
918
919     if(!count){
920         status = Ok;
921         goto end;
922     }
923     if(!pti || !tp || !ptcopy){
924         status = OutOfMemory;
925         goto end;
926     }
927
928     for(i = 1; i < count; i++){
929         if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
930             if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
931                 || !(types[i + 1] & PathPointTypeBezier)){
932                 ERR("Bad bezier points\n");
933                 goto end;
934             }
935             i += 2;
936         }
937     }
938
939     memcpy(ptcopy, pt, count * sizeof(GpPointF));
940
941     /* If we are drawing caps, go through the points and adjust them accordingly,
942      * and draw the caps. */
943     if(caps){
944         switch(types[count - 1] & PathPointTypePathTypeMask){
945             case PathPointTypeBezier:
946                 if(pen->endcap == LineCapArrowAnchor)
947                     shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
948                 else if((pen->endcap == LineCapCustom) && pen->customend)
949                     shorten_bezier_amt(&ptcopy[count - 4],
950                                        pen->width * pen->customend->inset, FALSE);
951
952                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
953                     pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
954                     pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
955                     pt[count - 1].X, pt[count - 1].Y);
956
957                 break;
958             case PathPointTypeLine:
959                 if(pen->endcap == LineCapArrowAnchor)
960                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
961                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
962                                      pen->width);
963                 else if((pen->endcap == LineCapCustom) && pen->customend)
964                     shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
965                                      &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
966                                      pen->customend->inset * pen->width);
967
968                 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
969                          pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
970                          pt[count - 1].Y);
971
972                 break;
973             default:
974                 ERR("Bad path last point\n");
975                 goto end;
976         }
977
978         /* Find start of points */
979         for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
980             == PathPointTypeStart); j++);
981
982         switch(types[j] & PathPointTypePathTypeMask){
983             case PathPointTypeBezier:
984                 if(pen->startcap == LineCapArrowAnchor)
985                     shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
986                 else if((pen->startcap == LineCapCustom) && pen->customstart)
987                     shorten_bezier_amt(&ptcopy[j - 1],
988                                        pen->width * pen->customstart->inset, TRUE);
989
990                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
991                     pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
992                     pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
993                     pt[j - 1].X, pt[j - 1].Y);
994
995                 break;
996             case PathPointTypeLine:
997                 if(pen->startcap == LineCapArrowAnchor)
998                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
999                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1000                                      pen->width);
1001                 else if((pen->startcap == LineCapCustom) && pen->customstart)
1002                     shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
1003                                      &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
1004                                      pen->customstart->inset * pen->width);
1005
1006                 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
1007                          pt[j].X, pt[j].Y, pt[j - 1].X,
1008                          pt[j - 1].Y);
1009
1010                 break;
1011             default:
1012                 ERR("Bad path points\n");
1013                 goto end;
1014         }
1015     }
1016
1017     transform_and_round_points(graphics, pti, ptcopy, count);
1018
1019     for(i = 0; i < count; i++){
1020         tp[i] = convert_path_point_type(types[i]);
1021     }
1022
1023     PolyDraw(graphics->hdc, pti, tp, count);
1024
1025     status = Ok;
1026
1027 end:
1028     GdipFree(pti);
1029     GdipFree(ptcopy);
1030     GdipFree(tp);
1031
1032     return status;
1033 }
1034
1035 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
1036 {
1037     GpStatus result;
1038
1039     BeginPath(graphics->hdc);
1040     result = draw_poly(graphics, NULL, path->pathdata.Points,
1041                        path->pathdata.Types, path->pathdata.Count, FALSE);
1042     EndPath(graphics->hdc);
1043     return result;
1044 }
1045
1046 typedef struct _GraphicsContainerItem {
1047     struct list entry;
1048     GraphicsContainer contid;
1049
1050     SmoothingMode smoothing;
1051     CompositingQuality compqual;
1052     InterpolationMode interpolation;
1053     CompositingMode compmode;
1054     TextRenderingHint texthint;
1055     REAL scale;
1056     GpUnit unit;
1057     PixelOffsetMode pixeloffset;
1058     UINT textcontrast;
1059     GpMatrix* worldtrans;
1060     GpRegion* clip;
1061 } GraphicsContainerItem;
1062
1063 static GpStatus init_container(GraphicsContainerItem** container,
1064         GDIPCONST GpGraphics* graphics){
1065     GpStatus sts;
1066
1067     *container = GdipAlloc(sizeof(GraphicsContainerItem));
1068     if(!(*container))
1069         return OutOfMemory;
1070
1071     (*container)->contid = graphics->contid + 1;
1072
1073     (*container)->smoothing = graphics->smoothing;
1074     (*container)->compqual = graphics->compqual;
1075     (*container)->interpolation = graphics->interpolation;
1076     (*container)->compmode = graphics->compmode;
1077     (*container)->texthint = graphics->texthint;
1078     (*container)->scale = graphics->scale;
1079     (*container)->unit = graphics->unit;
1080     (*container)->textcontrast = graphics->textcontrast;
1081     (*container)->pixeloffset = graphics->pixeloffset;
1082
1083     sts = GdipCloneMatrix(graphics->worldtrans, &(*container)->worldtrans);
1084     if(sts != Ok){
1085         GdipFree(*container);
1086         *container = NULL;
1087         return sts;
1088     }
1089
1090     sts = GdipCloneRegion(graphics->clip, &(*container)->clip);
1091     if(sts != Ok){
1092         GdipDeleteMatrix((*container)->worldtrans);
1093         GdipFree(*container);
1094         *container = NULL;
1095         return sts;
1096     }
1097
1098     return Ok;
1099 }
1100
1101 static void delete_container(GraphicsContainerItem* container){
1102     GdipDeleteMatrix(container->worldtrans);
1103     GdipDeleteRegion(container->clip);
1104     GdipFree(container);
1105 }
1106
1107 static GpStatus restore_container(GpGraphics* graphics,
1108         GDIPCONST GraphicsContainerItem* container){
1109     GpStatus sts;
1110     GpMatrix *newTrans;
1111     GpRegion *newClip;
1112
1113     sts = GdipCloneMatrix(container->worldtrans, &newTrans);
1114     if(sts != Ok)
1115         return sts;
1116
1117     sts = GdipCloneRegion(container->clip, &newClip);
1118     if(sts != Ok){
1119         GdipDeleteMatrix(newTrans);
1120         return sts;
1121     }
1122
1123     GdipDeleteMatrix(graphics->worldtrans);
1124     graphics->worldtrans = newTrans;
1125
1126     GdipDeleteRegion(graphics->clip);
1127     graphics->clip = newClip;
1128
1129     graphics->contid = container->contid - 1;
1130
1131     graphics->smoothing = container->smoothing;
1132     graphics->compqual = container->compqual;
1133     graphics->interpolation = container->interpolation;
1134     graphics->compmode = container->compmode;
1135     graphics->texthint = container->texthint;
1136     graphics->scale = container->scale;
1137     graphics->unit = container->unit;
1138     graphics->textcontrast = container->textcontrast;
1139     graphics->pixeloffset = container->pixeloffset;
1140
1141     return Ok;
1142 }
1143
1144 static GpStatus get_graphics_bounds(GpGraphics* graphics, GpRectF* rect)
1145 {
1146     RECT wnd_rect;
1147     GpStatus stat=Ok;
1148     GpUnit unit;
1149
1150     if(graphics->hwnd) {
1151         if(!GetClientRect(graphics->hwnd, &wnd_rect))
1152             return GenericError;
1153
1154         rect->X = wnd_rect.left;
1155         rect->Y = wnd_rect.top;
1156         rect->Width = wnd_rect.right - wnd_rect.left;
1157         rect->Height = wnd_rect.bottom - wnd_rect.top;
1158     }else if (graphics->image){
1159         stat = GdipGetImageBounds(graphics->image, rect, &unit);
1160         if (stat == Ok && unit != UnitPixel)
1161             FIXME("need to convert from unit %i\n", unit);
1162     }else{
1163         rect->X = 0;
1164         rect->Y = 0;
1165         rect->Width = GetDeviceCaps(graphics->hdc, HORZRES);
1166         rect->Height = GetDeviceCaps(graphics->hdc, VERTRES);
1167     }
1168
1169     return stat;
1170 }
1171
1172 /* on success, rgn will contain the region of the graphics object which
1173  * is visible after clipping has been applied */
1174 static GpStatus get_visible_clip_region(GpGraphics *graphics, GpRegion *rgn)
1175 {
1176     GpStatus stat;
1177     GpRectF rectf;
1178     GpRegion* tmp;
1179
1180     if((stat = get_graphics_bounds(graphics, &rectf)) != Ok)
1181         return stat;
1182
1183     if((stat = GdipCreateRegion(&tmp)) != Ok)
1184         return stat;
1185
1186     if((stat = GdipCombineRegionRect(tmp, &rectf, CombineModeReplace)) != Ok)
1187         goto end;
1188
1189     if((stat = GdipCombineRegionRegion(tmp, graphics->clip, CombineModeIntersect)) != Ok)
1190         goto end;
1191
1192     stat = GdipCombineRegionRegion(rgn, tmp, CombineModeReplace);
1193
1194 end:
1195     GdipDeleteRegion(tmp);
1196     return stat;
1197 }
1198
1199 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
1200 {
1201     TRACE("(%p, %p)\n", hdc, graphics);
1202
1203     return GdipCreateFromHDC2(hdc, NULL, graphics);
1204 }
1205
1206 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
1207 {
1208     GpStatus retval;
1209
1210     TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
1211
1212     if(hDevice != NULL) {
1213         FIXME("Don't know how to handle parameter hDevice\n");
1214         return NotImplemented;
1215     }
1216
1217     if(hdc == NULL)
1218         return OutOfMemory;
1219
1220     if(graphics == NULL)
1221         return InvalidParameter;
1222
1223     *graphics = GdipAlloc(sizeof(GpGraphics));
1224     if(!*graphics)  return OutOfMemory;
1225
1226     if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
1227         GdipFree(*graphics);
1228         return retval;
1229     }
1230
1231     if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
1232         GdipFree((*graphics)->worldtrans);
1233         GdipFree(*graphics);
1234         return retval;
1235     }
1236
1237     (*graphics)->hdc = hdc;
1238     (*graphics)->hwnd = WindowFromDC(hdc);
1239     (*graphics)->owndc = FALSE;
1240     (*graphics)->smoothing = SmoothingModeDefault;
1241     (*graphics)->compqual = CompositingQualityDefault;
1242     (*graphics)->interpolation = InterpolationModeDefault;
1243     (*graphics)->pixeloffset = PixelOffsetModeDefault;
1244     (*graphics)->compmode = CompositingModeSourceOver;
1245     (*graphics)->unit = UnitDisplay;
1246     (*graphics)->scale = 1.0;
1247     (*graphics)->busy = FALSE;
1248     (*graphics)->textcontrast = 4;
1249     list_init(&(*graphics)->containers);
1250     (*graphics)->contid = 0;
1251
1252     TRACE("<-- %p\n", *graphics);
1253
1254     return Ok;
1255 }
1256
1257 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
1258 {
1259     GpStatus ret;
1260     HDC hdc;
1261
1262     TRACE("(%p, %p)\n", hwnd, graphics);
1263
1264     hdc = GetDC(hwnd);
1265
1266     if((ret = GdipCreateFromHDC(hdc, graphics)) != Ok)
1267     {
1268         ReleaseDC(hwnd, hdc);
1269         return ret;
1270     }
1271
1272     (*graphics)->hwnd = hwnd;
1273     (*graphics)->owndc = TRUE;
1274
1275     return Ok;
1276 }
1277
1278 /* FIXME: no icm handling */
1279 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
1280 {
1281     TRACE("(%p, %p)\n", hwnd, graphics);
1282
1283     return GdipCreateFromHWND(hwnd, graphics);
1284 }
1285
1286 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
1287     GpMetafile **metafile)
1288 {
1289     static int calls;
1290
1291     TRACE("(%p,%i,%p)\n", hemf, delete, metafile);
1292
1293     if(!hemf || !metafile)
1294         return InvalidParameter;
1295
1296     if(!(calls++))
1297         FIXME("not implemented\n");
1298
1299     return NotImplemented;
1300 }
1301
1302 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
1303     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1304 {
1305     IStream *stream = NULL;
1306     UINT read;
1307     BYTE* copy;
1308     HENHMETAFILE hemf;
1309     GpStatus retval = Ok;
1310
1311     TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
1312
1313     if(!hwmf || !metafile || !placeable)
1314         return InvalidParameter;
1315
1316     *metafile = NULL;
1317     read = GetMetaFileBitsEx(hwmf, 0, NULL);
1318     if(!read)
1319         return GenericError;
1320     copy = GdipAlloc(read);
1321     GetMetaFileBitsEx(hwmf, read, copy);
1322
1323     hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
1324     GdipFree(copy);
1325
1326     read = GetEnhMetaFileBits(hemf, 0, NULL);
1327     copy = GdipAlloc(read);
1328     GetEnhMetaFileBits(hemf, read, copy);
1329     DeleteEnhMetaFile(hemf);
1330
1331     if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
1332         ERR("could not make stream\n");
1333         GdipFree(copy);
1334         retval = GenericError;
1335         goto err;
1336     }
1337
1338     *metafile = GdipAlloc(sizeof(GpMetafile));
1339     if(!*metafile){
1340         retval = OutOfMemory;
1341         goto err;
1342     }
1343
1344     if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
1345         (LPVOID*) &((*metafile)->image.picture)) != S_OK)
1346     {
1347         retval = GenericError;
1348         goto err;
1349     }
1350
1351
1352     (*metafile)->image.type = ImageTypeMetafile;
1353     memcpy(&(*metafile)->image.format, &ImageFormatWMF, sizeof(GUID));
1354     (*metafile)->image.palette_flags = 0;
1355     (*metafile)->image.palette_count = 0;
1356     (*metafile)->image.palette_size = 0;
1357     (*metafile)->image.palette_entries = NULL;
1358     (*metafile)->image.xres = (REAL)placeable->Inch;
1359     (*metafile)->image.yres = (REAL)placeable->Inch;
1360     (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
1361     (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Top) / ((REAL) placeable->Inch);
1362     (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
1363                     - placeable->BoundingBox.Left));
1364     (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
1365                    - placeable->BoundingBox.Top));
1366     (*metafile)->unit = UnitPixel;
1367
1368     if(delete)
1369         DeleteMetaFile(hwmf);
1370
1371     TRACE("<-- %p\n", *metafile);
1372
1373 err:
1374     if (retval != Ok)
1375         GdipFree(*metafile);
1376     IStream_Release(stream);
1377     return retval;
1378 }
1379
1380 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
1381     GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
1382 {
1383     HMETAFILE hmf = GetMetaFileW(file);
1384
1385     TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
1386
1387     if(!hmf) return InvalidParameter;
1388
1389     return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
1390 }
1391
1392 GpStatus WINGDIPAPI GdipCreateMetafileFromFile(GDIPCONST WCHAR *file,
1393     GpMetafile **metafile)
1394 {
1395     FIXME("(%p, %p): stub\n", file, metafile);
1396     return NotImplemented;
1397 }
1398
1399 GpStatus WINGDIPAPI GdipCreateMetafileFromStream(IStream *stream,
1400     GpMetafile **metafile)
1401 {
1402     FIXME("(%p, %p): stub\n", stream, metafile);
1403     return NotImplemented;
1404 }
1405
1406 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
1407     UINT access, IStream **stream)
1408 {
1409     DWORD dwMode;
1410     HRESULT ret;
1411
1412     TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
1413
1414     if(!stream || !filename)
1415         return InvalidParameter;
1416
1417     if(access & GENERIC_WRITE)
1418         dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
1419     else if(access & GENERIC_READ)
1420         dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
1421     else
1422         return InvalidParameter;
1423
1424     ret = SHCreateStreamOnFileW(filename, dwMode, stream);
1425
1426     return hresult_to_status(ret);
1427 }
1428
1429 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
1430 {
1431     GraphicsContainerItem *cont, *next;
1432     TRACE("(%p)\n", graphics);
1433
1434     if(!graphics) return InvalidParameter;
1435     if(graphics->busy) return ObjectBusy;
1436
1437     if(graphics->owndc)
1438         ReleaseDC(graphics->hwnd, graphics->hdc);
1439
1440     LIST_FOR_EACH_ENTRY_SAFE(cont, next, &graphics->containers, GraphicsContainerItem, entry){
1441         list_remove(&cont->entry);
1442         delete_container(cont);
1443     }
1444
1445     GdipDeleteRegion(graphics->clip);
1446     GdipDeleteMatrix(graphics->worldtrans);
1447     GdipFree(graphics);
1448
1449     return Ok;
1450 }
1451
1452 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
1453     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1454 {
1455     INT save_state, num_pts;
1456     GpPointF points[MAX_ARC_PTS];
1457     GpStatus retval;
1458
1459     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1460           width, height, startAngle, sweepAngle);
1461
1462     if(!graphics || !pen || width <= 0 || height <= 0)
1463         return InvalidParameter;
1464
1465     if(graphics->busy)
1466         return ObjectBusy;
1467
1468     num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
1469
1470     save_state = prepare_dc(graphics, pen);
1471
1472     retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
1473
1474     restore_dc(graphics, save_state);
1475
1476     return retval;
1477 }
1478
1479 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
1480     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1481 {
1482     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1483           width, height, startAngle, sweepAngle);
1484
1485     return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1486 }
1487
1488 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
1489     REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
1490 {
1491     INT save_state;
1492     GpPointF pt[4];
1493     GpStatus retval;
1494
1495     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
1496           x2, y2, x3, y3, x4, y4);
1497
1498     if(!graphics || !pen)
1499         return InvalidParameter;
1500
1501     if(graphics->busy)
1502         return ObjectBusy;
1503
1504     pt[0].X = x1;
1505     pt[0].Y = y1;
1506     pt[1].X = x2;
1507     pt[1].Y = y2;
1508     pt[2].X = x3;
1509     pt[2].Y = y3;
1510     pt[3].X = x4;
1511     pt[3].Y = y4;
1512
1513     save_state = prepare_dc(graphics, pen);
1514
1515     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1516
1517     restore_dc(graphics, save_state);
1518
1519     return retval;
1520 }
1521
1522 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
1523     INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
1524 {
1525     INT save_state;
1526     GpPointF pt[4];
1527     GpStatus retval;
1528
1529     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1530           x2, y2, x3, y3, x4, y4);
1531
1532     if(!graphics || !pen)
1533         return InvalidParameter;
1534
1535     if(graphics->busy)
1536         return ObjectBusy;
1537
1538     pt[0].X = x1;
1539     pt[0].Y = y1;
1540     pt[1].X = x2;
1541     pt[1].Y = y2;
1542     pt[2].X = x3;
1543     pt[2].Y = y3;
1544     pt[3].X = x4;
1545     pt[3].Y = y4;
1546
1547     save_state = prepare_dc(graphics, pen);
1548
1549     retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1550
1551     restore_dc(graphics, save_state);
1552
1553     return retval;
1554 }
1555
1556 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1557     GDIPCONST GpPointF *points, INT count)
1558 {
1559     INT i;
1560     GpStatus ret;
1561
1562     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1563
1564     if(!graphics || !pen || !points || (count <= 0))
1565         return InvalidParameter;
1566
1567     if(graphics->busy)
1568         return ObjectBusy;
1569
1570     for(i = 0; i < floor(count / 4); i++){
1571         ret = GdipDrawBezier(graphics, pen,
1572                              points[4*i].X, points[4*i].Y,
1573                              points[4*i + 1].X, points[4*i + 1].Y,
1574                              points[4*i + 2].X, points[4*i + 2].Y,
1575                              points[4*i + 3].X, points[4*i + 3].Y);
1576         if(ret != Ok)
1577             return ret;
1578     }
1579
1580     return Ok;
1581 }
1582
1583 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1584     GDIPCONST GpPoint *points, INT count)
1585 {
1586     GpPointF *pts;
1587     GpStatus ret;
1588     INT i;
1589
1590     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1591
1592     if(!graphics || !pen || !points || (count <= 0))
1593         return InvalidParameter;
1594
1595     if(graphics->busy)
1596         return ObjectBusy;
1597
1598     pts = GdipAlloc(sizeof(GpPointF) * count);
1599     if(!pts)
1600         return OutOfMemory;
1601
1602     for(i = 0; i < count; i++){
1603         pts[i].X = (REAL)points[i].X;
1604         pts[i].Y = (REAL)points[i].Y;
1605     }
1606
1607     ret = GdipDrawBeziers(graphics,pen,pts,count);
1608
1609     GdipFree(pts);
1610
1611     return ret;
1612 }
1613
1614 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1615     GDIPCONST GpPointF *points, INT count)
1616 {
1617     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1618
1619     return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1620 }
1621
1622 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1623     GDIPCONST GpPoint *points, INT count)
1624 {
1625     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1626
1627     return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1628 }
1629
1630 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1631     GDIPCONST GpPointF *points, INT count, REAL tension)
1632 {
1633     GpPath *path;
1634     GpStatus stat;
1635
1636     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1637
1638     if(!graphics || !pen || !points || count <= 0)
1639         return InvalidParameter;
1640
1641     if(graphics->busy)
1642         return ObjectBusy;
1643
1644     if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1645         return stat;
1646
1647     stat = GdipAddPathClosedCurve2(path, points, count, tension);
1648     if(stat != Ok){
1649         GdipDeletePath(path);
1650         return stat;
1651     }
1652
1653     stat = GdipDrawPath(graphics, pen, path);
1654
1655     GdipDeletePath(path);
1656
1657     return stat;
1658 }
1659
1660 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1661     GDIPCONST GpPoint *points, INT count, REAL tension)
1662 {
1663     GpPointF *ptf;
1664     GpStatus stat;
1665     INT i;
1666
1667     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1668
1669     if(!points || count <= 0)
1670         return InvalidParameter;
1671
1672     ptf = GdipAlloc(sizeof(GpPointF)*count);
1673     if(!ptf)
1674         return OutOfMemory;
1675
1676     for(i = 0; i < count; i++){
1677         ptf[i].X = (REAL)points[i].X;
1678         ptf[i].Y = (REAL)points[i].Y;
1679     }
1680
1681     stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1682
1683     GdipFree(ptf);
1684
1685     return stat;
1686 }
1687
1688 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1689     GDIPCONST GpPointF *points, INT count)
1690 {
1691     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1692
1693     return GdipDrawCurve2(graphics,pen,points,count,1.0);
1694 }
1695
1696 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1697     GDIPCONST GpPoint *points, INT count)
1698 {
1699     GpPointF *pointsF;
1700     GpStatus ret;
1701     INT i;
1702
1703     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1704
1705     if(!points)
1706         return InvalidParameter;
1707
1708     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1709     if(!pointsF)
1710         return OutOfMemory;
1711
1712     for(i = 0; i < count; i++){
1713         pointsF[i].X = (REAL)points[i].X;
1714         pointsF[i].Y = (REAL)points[i].Y;
1715     }
1716
1717     ret = GdipDrawCurve(graphics,pen,pointsF,count);
1718     GdipFree(pointsF);
1719
1720     return ret;
1721 }
1722
1723 /* Approximates cardinal spline with Bezier curves. */
1724 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1725     GDIPCONST GpPointF *points, INT count, REAL tension)
1726 {
1727     /* PolyBezier expects count*3-2 points. */
1728     INT i, len_pt = count*3-2, save_state;
1729     GpPointF *pt;
1730     REAL x1, x2, y1, y2;
1731     GpStatus retval;
1732
1733     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1734
1735     if(!graphics || !pen)
1736         return InvalidParameter;
1737
1738     if(graphics->busy)
1739         return ObjectBusy;
1740
1741     if(count < 2)
1742         return InvalidParameter;
1743
1744     pt = GdipAlloc(len_pt * sizeof(GpPointF));
1745     if(!pt)
1746         return OutOfMemory;
1747
1748     tension = tension * TENSION_CONST;
1749
1750     calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1751         tension, &x1, &y1);
1752
1753     pt[0].X = points[0].X;
1754     pt[0].Y = points[0].Y;
1755     pt[1].X = x1;
1756     pt[1].Y = y1;
1757
1758     for(i = 0; i < count-2; i++){
1759         calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1760
1761         pt[3*i+2].X = x1;
1762         pt[3*i+2].Y = y1;
1763         pt[3*i+3].X = points[i+1].X;
1764         pt[3*i+3].Y = points[i+1].Y;
1765         pt[3*i+4].X = x2;
1766         pt[3*i+4].Y = y2;
1767     }
1768
1769     calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
1770         points[count-2].X, points[count-2].Y, tension, &x1, &y1);
1771
1772     pt[len_pt-2].X = x1;
1773     pt[len_pt-2].Y = y1;
1774     pt[len_pt-1].X = points[count-1].X;
1775     pt[len_pt-1].Y = points[count-1].Y;
1776
1777     save_state = prepare_dc(graphics, pen);
1778
1779     retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1780
1781     GdipFree(pt);
1782     restore_dc(graphics, save_state);
1783
1784     return retval;
1785 }
1786
1787 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1788     GDIPCONST GpPoint *points, INT count, REAL tension)
1789 {
1790     GpPointF *pointsF;
1791     GpStatus ret;
1792     INT i;
1793
1794     TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1795
1796     if(!points)
1797         return InvalidParameter;
1798
1799     pointsF = GdipAlloc(sizeof(GpPointF)*count);
1800     if(!pointsF)
1801         return OutOfMemory;
1802
1803     for(i = 0; i < count; i++){
1804         pointsF[i].X = (REAL)points[i].X;
1805         pointsF[i].Y = (REAL)points[i].Y;
1806     }
1807
1808     ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1809     GdipFree(pointsF);
1810
1811     return ret;
1812 }
1813
1814 GpStatus WINGDIPAPI GdipDrawCurve3(GpGraphics *graphics, GpPen *pen,
1815     GDIPCONST GpPointF *points, INT count, INT offset, INT numberOfSegments,
1816     REAL tension)
1817 {
1818     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1819
1820     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1821         return InvalidParameter;
1822     }
1823
1824     return GdipDrawCurve2(graphics, pen, points + offset, numberOfSegments + 1, tension);
1825 }
1826
1827 GpStatus WINGDIPAPI GdipDrawCurve3I(GpGraphics *graphics, GpPen *pen,
1828     GDIPCONST GpPoint *points, INT count, INT offset, INT numberOfSegments,
1829     REAL tension)
1830 {
1831     TRACE("(%p, %p, %p, %d, %d, %d, %.2f)\n", graphics, pen, points, count, offset, numberOfSegments, tension);
1832
1833     if(count < 0){
1834         return OutOfMemory;
1835     }
1836
1837     if(offset >= count || numberOfSegments > count - offset - 1 || numberOfSegments <= 0){
1838         return InvalidParameter;
1839     }
1840
1841     return GdipDrawCurve2I(graphics, pen, points + offset, numberOfSegments + 1, tension);
1842 }
1843
1844 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1845     REAL y, REAL width, REAL height)
1846 {
1847     INT save_state;
1848     GpPointF ptf[2];
1849     POINT pti[2];
1850
1851     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1852
1853     if(!graphics || !pen)
1854         return InvalidParameter;
1855
1856     if(graphics->busy)
1857         return ObjectBusy;
1858
1859     ptf[0].X = x;
1860     ptf[0].Y = y;
1861     ptf[1].X = x + width;
1862     ptf[1].Y = y + height;
1863
1864     save_state = prepare_dc(graphics, pen);
1865     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1866
1867     transform_and_round_points(graphics, pti, ptf, 2);
1868
1869     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1870
1871     restore_dc(graphics, save_state);
1872
1873     return Ok;
1874 }
1875
1876 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1877     INT y, INT width, INT height)
1878 {
1879     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1880
1881     return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1882 }
1883
1884
1885 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1886 {
1887     UINT width, height;
1888     GpPointF points[3];
1889
1890     TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1891
1892     if(!graphics || !image)
1893         return InvalidParameter;
1894
1895     GdipGetImageWidth(image, &width);
1896     GdipGetImageHeight(image, &height);
1897
1898     /* FIXME: we should use the graphics and image dpi, somehow */
1899
1900     points[0].X = points[2].X = x;
1901     points[0].Y = points[1].Y = y;
1902     points[1].X = x + width;
1903     points[2].Y = y + height;
1904
1905     return GdipDrawImagePointsRect(graphics, image, points, 3, 0, 0, width, height,
1906         UnitPixel, NULL, NULL, NULL);
1907 }
1908
1909 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1910     INT y)
1911 {
1912     TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1913
1914     return GdipDrawImage(graphics, image, (REAL)x, (REAL)y);
1915 }
1916
1917 GpStatus WINGDIPAPI GdipDrawImagePointRect(GpGraphics *graphics, GpImage *image,
1918     REAL x, REAL y, REAL srcx, REAL srcy, REAL srcwidth, REAL srcheight,
1919     GpUnit srcUnit)
1920 {
1921     GpPointF points[3];
1922     TRACE("(%p, %p, %f, %f, %f, %f, %f, %f, %d)\n", graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1923
1924     points[0].X = points[2].X = x;
1925     points[0].Y = points[1].Y = y;
1926
1927     /* FIXME: convert image coordinates to Graphics coordinates? */
1928     points[1].X = x + srcwidth;
1929     points[2].Y = y + srcheight;
1930
1931     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1932         srcwidth, srcheight, srcUnit, NULL, NULL, NULL);
1933 }
1934
1935 GpStatus WINGDIPAPI GdipDrawImagePointRectI(GpGraphics *graphics, GpImage *image,
1936     INT x, INT y, INT srcx, INT srcy, INT srcwidth, INT srcheight,
1937     GpUnit srcUnit)
1938 {
1939     return GdipDrawImagePointRect(graphics, image, x, y, srcx, srcy, srcwidth, srcheight, srcUnit);
1940 }
1941
1942 GpStatus WINGDIPAPI GdipDrawImagePoints(GpGraphics *graphics, GpImage *image,
1943     GDIPCONST GpPointF *dstpoints, INT count)
1944 {
1945     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1946     return NotImplemented;
1947 }
1948
1949 GpStatus WINGDIPAPI GdipDrawImagePointsI(GpGraphics *graphics, GpImage *image,
1950     GDIPCONST GpPoint *dstpoints, INT count)
1951 {
1952     FIXME("(%p, %p, %p, %d): stub\n", graphics, image, dstpoints, count);
1953     return NotImplemented;
1954 }
1955
1956 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1957      GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1958      REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1959      DrawImageAbort callback, VOID * callbackData)
1960 {
1961     GpPointF ptf[4];
1962     POINT pti[4];
1963     REAL dx, dy;
1964     GpStatus stat;
1965
1966     TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1967           count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1968           callbackData);
1969
1970     if(!graphics || !image || !points || count != 3)
1971          return InvalidParameter;
1972
1973     TRACE("%s %s %s\n", debugstr_pointf(&points[0]), debugstr_pointf(&points[1]),
1974         debugstr_pointf(&points[2]));
1975
1976     memcpy(ptf, points, 3 * sizeof(GpPointF));
1977     ptf[3].X = ptf[2].X + ptf[1].X - ptf[0].X;
1978     ptf[3].Y = ptf[2].Y + ptf[1].Y - ptf[0].Y;
1979     transform_and_round_points(graphics, pti, ptf, 4);
1980
1981     if (image->picture)
1982     {
1983         if (!graphics->hdc)
1984         {
1985             FIXME("graphics object has no HDC\n");
1986         }
1987
1988         /* FIXME: partially implemented (only works for rectangular parallelograms) */
1989         if(srcUnit == UnitInch)
1990             dx = dy = (REAL) INCH_HIMETRIC;
1991         else if(srcUnit == UnitPixel){
1992             dx = ((REAL) INCH_HIMETRIC) /
1993                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1994             dy = ((REAL) INCH_HIMETRIC) /
1995                  ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1996         }
1997         else
1998             return NotImplemented;
1999
2000         if(IPicture_Render(image->picture, graphics->hdc,
2001             pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
2002             srcx * dx, srcy * dy,
2003             srcwidth * dx, srcheight * dy,
2004             NULL) != S_OK){
2005             if(callback)
2006                 callback(callbackData);
2007             return GenericError;
2008         }
2009     }
2010     else if (image->type == ImageTypeBitmap && ((GpBitmap*)image)->hbitmap)
2011     {
2012         GpBitmap* bitmap = (GpBitmap*)image;
2013         int use_software=0;
2014
2015         if (srcUnit == UnitInch)
2016             dx = dy = 96.0; /* FIXME: use the image resolution */
2017         else if (srcUnit == UnitPixel)
2018             dx = dy = 1.0;
2019         else
2020             return NotImplemented;
2021
2022         if (imageAttributes ||
2023             (graphics->image && graphics->image->type == ImageTypeBitmap) ||
2024             ptf[1].Y != ptf[0].Y || ptf[2].X != ptf[0].X)
2025             use_software = 1;
2026
2027         if (use_software)
2028         {
2029             RECT src_area, dst_area;
2030             int i, x, y, stride;
2031             GpMatrix *dst_to_src;
2032             REAL m11, m12, m21, m22, mdx, mdy;
2033             LPBYTE data;
2034
2035             src_area.left = srcx*dx;
2036             src_area.top = srcy*dy;
2037             src_area.right = (srcx+srcwidth)*dx;
2038             src_area.bottom = (srcy+srcheight)*dy;
2039
2040             dst_area.left = dst_area.right = pti[0].x;
2041             dst_area.top = dst_area.bottom = pti[0].y;
2042             for (i=1; i<4; i++)
2043             {
2044                 if (dst_area.left > pti[i].x) dst_area.left = pti[i].x;
2045                 if (dst_area.right < pti[i].x) dst_area.right = pti[i].x;
2046                 if (dst_area.top > pti[i].y) dst_area.top = pti[i].y;
2047                 if (dst_area.bottom < pti[i].y) dst_area.bottom = pti[i].y;
2048             }
2049
2050             m11 = (ptf[1].X - ptf[0].X) / srcwidth;
2051             m21 = (ptf[2].X - ptf[0].X) / srcheight;
2052             mdx = ptf[0].X - m11 * srcx - m21 * srcy;
2053             m12 = (ptf[1].Y - ptf[0].Y) / srcwidth;
2054             m22 = (ptf[2].Y - ptf[0].Y) / srcheight;
2055             mdy = ptf[0].Y - m12 * srcx - m22 * srcy;
2056
2057             stat = GdipCreateMatrix2(m11, m12, m21, m22, mdx, mdy, &dst_to_src);
2058             if (stat != Ok) return stat;
2059
2060             stat = GdipInvertMatrix(dst_to_src);
2061             if (stat != Ok)
2062             {
2063                 GdipDeleteMatrix(dst_to_src);
2064                 return stat;
2065             }
2066
2067             data = GdipAlloc(sizeof(ARGB) * (dst_area.right - dst_area.left) * (dst_area.bottom - dst_area.top));
2068             if (!data)
2069             {
2070                 GdipDeleteMatrix(dst_to_src);
2071                 return OutOfMemory;
2072             }
2073
2074             stride = sizeof(ARGB) * (dst_area.right - dst_area.left);
2075
2076             if (imageAttributes &&
2077                 (imageAttributes->wrap != WrapModeClamp ||
2078                  imageAttributes->outside_color != 0x00000000 ||
2079                  imageAttributes->clamp))
2080             {
2081                 static int fixme;
2082                 if (!fixme++)
2083                     FIXME("Image wrap mode not implemented\n");
2084             }
2085
2086             for (x=dst_area.left; x<dst_area.right; x++)
2087             {
2088                 for (y=dst_area.top; y<dst_area.bottom; y++)
2089                 {
2090                     GpPointF src_pointf;
2091                     int src_x, src_y;
2092                     ARGB *src_color;
2093
2094                     src_pointf.X = x;
2095                     src_pointf.Y = y;
2096
2097                     GdipTransformMatrixPoints(dst_to_src, &src_pointf, 1);
2098
2099                     src_x = roundr(src_pointf.X);
2100                     src_y = roundr(src_pointf.Y);
2101
2102                     src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2103
2104                     if (src_x < src_area.left || src_x >= src_area.right ||
2105                         src_y < src_area.top || src_y >= src_area.bottom)
2106                         *src_color = 0;
2107                     else
2108                         GdipBitmapGetPixel(bitmap, src_x, src_y, src_color);
2109                 }
2110             }
2111
2112             GdipDeleteMatrix(dst_to_src);
2113
2114             if (imageAttributes)
2115             {
2116                 if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled ||
2117                     imageAttributes->colorkeys[ColorAdjustTypeDefault].enabled)
2118                 {
2119                     const struct color_key *key;
2120                     BYTE min_blue, min_green, min_red;
2121                     BYTE max_blue, max_green, max_red;
2122
2123                     if (imageAttributes->colorkeys[ColorAdjustTypeBitmap].enabled)
2124                         key = &imageAttributes->colorkeys[ColorAdjustTypeBitmap];
2125                     else
2126                         key = &imageAttributes->colorkeys[ColorAdjustTypeDefault];
2127
2128                     min_blue = key->low&0xff;
2129                     min_green = (key->low>>8)&0xff;
2130                     min_red = (key->low>>16)&0xff;
2131
2132                     max_blue = key->high&0xff;
2133                     max_green = (key->high>>8)&0xff;
2134                     max_red = (key->high>>16)&0xff;
2135
2136                     for (x=dst_area.left; x<dst_area.right; x++)
2137                         for (y=dst_area.top; y<dst_area.bottom; y++)
2138                         {
2139                             ARGB *src_color;
2140                             BYTE blue, green, red;
2141                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2142                             blue = *src_color&0xff;
2143                             green = (*src_color>>8)&0xff;
2144                             red = (*src_color>>16)&0xff;
2145                             if (blue >= min_blue && green >= min_green && red >= min_red &&
2146                                 blue <= max_blue && green <= max_green && red <= max_red)
2147                                 *src_color = 0x00000000;
2148                         }
2149                 }
2150
2151                 if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled ||
2152                     imageAttributes->colorremaptables[ColorAdjustTypeDefault].enabled)
2153                 {
2154                     const struct color_remap_table *table;
2155
2156                     if (imageAttributes->colorremaptables[ColorAdjustTypeBitmap].enabled)
2157                         table = &imageAttributes->colorremaptables[ColorAdjustTypeBitmap];
2158                     else
2159                         table = &imageAttributes->colorremaptables[ColorAdjustTypeDefault];
2160
2161                     for (x=dst_area.left; x<dst_area.right; x++)
2162                         for (y=dst_area.top; y<dst_area.bottom; y++)
2163                         {
2164                             ARGB *src_color;
2165                             src_color = (ARGB*)(data + stride * (y - dst_area.top) + sizeof(ARGB) * (x - dst_area.left));
2166                             for (i=0; i<table->mapsize; i++)
2167                             {
2168                                 if (*src_color == table->colormap[i].oldColor.Argb)
2169                                 {
2170                                     *src_color = table->colormap[i].newColor.Argb;
2171                                     break;
2172                                 }
2173                             }
2174                         }
2175                 }
2176
2177                 if (imageAttributes->colormatrices[ColorAdjustTypeBitmap].enabled ||
2178                     imageAttributes->colormatrices[ColorAdjustTypeDefault].enabled)
2179                 {
2180                     static int fixme;
2181                     if (!fixme++)
2182                         FIXME("Color transforms not implemented\n");
2183                 }
2184
2185                 if (imageAttributes->gamma_enabled[ColorAdjustTypeBitmap] ||
2186                     imageAttributes->gamma_enabled[ColorAdjustTypeDefault])
2187                 {
2188                     static int fixme;
2189                     if (!fixme++)
2190                         FIXME("Gamma adjustment not implemented\n");
2191                 }
2192             }
2193
2194             stat = alpha_blend_pixels(graphics, dst_area.left, dst_area.top,
2195                 data, dst_area.right - dst_area.left, dst_area.bottom - dst_area.top, stride);
2196
2197             GdipFree(data);
2198
2199             return stat;
2200         }
2201         else
2202         {
2203             HDC hdc;
2204             int temp_hdc=0, temp_bitmap=0;
2205             HBITMAP hbitmap, old_hbm=NULL;
2206
2207             if (!(bitmap->format == PixelFormat16bppRGB555 ||
2208                   bitmap->format == PixelFormat24bppRGB ||
2209                   bitmap->format == PixelFormat32bppRGB ||
2210                   bitmap->format == PixelFormat32bppPARGB))
2211             {
2212                 BITMAPINFOHEADER bih;
2213                 BYTE *temp_bits;
2214                 PixelFormat dst_format;
2215
2216                 /* we can't draw a bitmap of this format directly */
2217                 hdc = CreateCompatibleDC(0);
2218                 temp_hdc = 1;
2219                 temp_bitmap = 1;
2220
2221                 bih.biSize = sizeof(BITMAPINFOHEADER);
2222                 bih.biWidth = bitmap->width;
2223                 bih.biHeight = -bitmap->height;
2224                 bih.biPlanes = 1;
2225                 bih.biBitCount = 32;
2226                 bih.biCompression = BI_RGB;
2227                 bih.biSizeImage = 0;
2228                 bih.biXPelsPerMeter = 0;
2229                 bih.biYPelsPerMeter = 0;
2230                 bih.biClrUsed = 0;
2231                 bih.biClrImportant = 0;
2232
2233                 hbitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bih, DIB_RGB_COLORS,
2234                     (void**)&temp_bits, NULL, 0);
2235
2236                 if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2237                     dst_format = PixelFormat32bppPARGB;
2238                 else
2239                     dst_format = PixelFormat32bppRGB;
2240
2241                 convert_pixels(bitmap->width, bitmap->height,
2242                     bitmap->width*4, temp_bits, dst_format,
2243                     bitmap->stride, bitmap->bits, bitmap->format, bitmap->image.palette_entries);
2244             }
2245             else
2246             {
2247                 hbitmap = bitmap->hbitmap;
2248                 hdc = bitmap->hdc;
2249                 temp_hdc = (hdc == 0);
2250             }
2251
2252             if (temp_hdc)
2253             {
2254                 if (!hdc) hdc = CreateCompatibleDC(0);
2255                 old_hbm = SelectObject(hdc, hbitmap);
2256             }
2257
2258             if (bitmap->format & (PixelFormatAlpha|PixelFormatPAlpha))
2259             {
2260                 BLENDFUNCTION bf;
2261
2262                 bf.BlendOp = AC_SRC_OVER;
2263                 bf.BlendFlags = 0;
2264                 bf.SourceConstantAlpha = 255;
2265                 bf.AlphaFormat = AC_SRC_ALPHA;
2266
2267                 GdiAlphaBlend(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2268                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, bf);
2269             }
2270             else
2271             {
2272                 StretchBlt(graphics->hdc, pti[0].x, pti[0].y, pti[1].x-pti[0].x, pti[2].y-pti[0].y,
2273                     hdc, srcx*dx, srcy*dy, srcwidth*dx, srcheight*dy, SRCCOPY);
2274             }
2275
2276             if (temp_hdc)
2277             {
2278                 SelectObject(hdc, old_hbm);
2279                 DeleteDC(hdc);
2280             }
2281
2282             if (temp_bitmap)
2283                 DeleteObject(hbitmap);
2284         }
2285     }
2286     else
2287     {
2288         ERR("GpImage with no IPicture or HBITMAP?!\n");
2289         return NotImplemented;
2290     }
2291
2292     return Ok;
2293 }
2294
2295 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
2296      GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
2297      INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
2298      DrawImageAbort callback, VOID * callbackData)
2299 {
2300     GpPointF pointsF[3];
2301     INT i;
2302
2303     TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
2304           srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
2305           callbackData);
2306
2307     if(!points || count!=3)
2308         return InvalidParameter;
2309
2310     for(i = 0; i < count; i++){
2311         pointsF[i].X = (REAL)points[i].X;
2312         pointsF[i].Y = (REAL)points[i].Y;
2313     }
2314
2315     return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
2316                                    (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
2317                                    callback, callbackData);
2318 }
2319
2320 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
2321     REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
2322     REAL srcwidth, REAL srcheight, GpUnit srcUnit,
2323     GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
2324     VOID * callbackData)
2325 {
2326     GpPointF points[3];
2327
2328     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
2329           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2330           srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2331
2332     points[0].X = dstx;
2333     points[0].Y = dsty;
2334     points[1].X = dstx + dstwidth;
2335     points[1].Y = dsty;
2336     points[2].X = dstx;
2337     points[2].Y = dsty + dstheight;
2338
2339     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2340                srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
2341 }
2342
2343 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
2344         INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
2345         INT srcwidth, INT srcheight, GpUnit srcUnit,
2346         GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
2347         VOID * callbackData)
2348 {
2349     GpPointF points[3];
2350
2351     TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
2352           graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
2353           srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2354
2355     points[0].X = dstx;
2356     points[0].Y = dsty;
2357     points[1].X = dstx + dstwidth;
2358     points[1].Y = dsty;
2359     points[2].X = dstx;
2360     points[2].Y = dsty + dstheight;
2361
2362     return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
2363                srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
2364 }
2365
2366 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
2367     REAL x, REAL y, REAL width, REAL height)
2368 {
2369     RectF bounds;
2370     GpUnit unit;
2371     GpStatus ret;
2372
2373     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
2374
2375     if(!graphics || !image)
2376         return InvalidParameter;
2377
2378     ret = GdipGetImageBounds(image, &bounds, &unit);
2379     if(ret != Ok)
2380         return ret;
2381
2382     return GdipDrawImageRectRect(graphics, image, x, y, width, height,
2383                                  bounds.X, bounds.Y, bounds.Width, bounds.Height,
2384                                  unit, NULL, NULL, NULL);
2385 }
2386
2387 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
2388     INT x, INT y, INT width, INT height)
2389 {
2390     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
2391
2392     return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
2393 }
2394
2395 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
2396     REAL y1, REAL x2, REAL y2)
2397 {
2398     INT save_state;
2399     GpPointF pt[2];
2400     GpStatus retval;
2401
2402     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
2403
2404     if(!pen || !graphics)
2405         return InvalidParameter;
2406
2407     if(graphics->busy)
2408         return ObjectBusy;
2409
2410     pt[0].X = x1;
2411     pt[0].Y = y1;
2412     pt[1].X = x2;
2413     pt[1].Y = y2;
2414
2415     save_state = prepare_dc(graphics, pen);
2416
2417     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2418
2419     restore_dc(graphics, save_state);
2420
2421     return retval;
2422 }
2423
2424 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
2425     INT y1, INT x2, INT y2)
2426 {
2427     INT save_state;
2428     GpPointF pt[2];
2429     GpStatus retval;
2430
2431     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
2432
2433     if(!pen || !graphics)
2434         return InvalidParameter;
2435
2436     if(graphics->busy)
2437         return ObjectBusy;
2438
2439     pt[0].X = (REAL)x1;
2440     pt[0].Y = (REAL)y1;
2441     pt[1].X = (REAL)x2;
2442     pt[1].Y = (REAL)y2;
2443
2444     save_state = prepare_dc(graphics, pen);
2445
2446     retval = draw_polyline(graphics, pen, pt, 2, TRUE);
2447
2448     restore_dc(graphics, save_state);
2449
2450     return retval;
2451 }
2452
2453 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
2454     GpPointF *points, INT count)
2455 {
2456     INT save_state;
2457     GpStatus retval;
2458
2459     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2460
2461     if(!pen || !graphics || (count < 2))
2462         return InvalidParameter;
2463
2464     if(graphics->busy)
2465         return ObjectBusy;
2466
2467     save_state = prepare_dc(graphics, pen);
2468
2469     retval = draw_polyline(graphics, pen, points, count, TRUE);
2470
2471     restore_dc(graphics, save_state);
2472
2473     return retval;
2474 }
2475
2476 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
2477     GpPoint *points, INT count)
2478 {
2479     INT save_state;
2480     GpStatus retval;
2481     GpPointF *ptf = NULL;
2482     int i;
2483
2484     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
2485
2486     if(!pen || !graphics || (count < 2))
2487         return InvalidParameter;
2488
2489     if(graphics->busy)
2490         return ObjectBusy;
2491
2492     ptf = GdipAlloc(count * sizeof(GpPointF));
2493     if(!ptf) return OutOfMemory;
2494
2495     for(i = 0; i < count; i ++){
2496         ptf[i].X = (REAL) points[i].X;
2497         ptf[i].Y = (REAL) points[i].Y;
2498     }
2499
2500     save_state = prepare_dc(graphics, pen);
2501
2502     retval = draw_polyline(graphics, pen, ptf, count, TRUE);
2503
2504     restore_dc(graphics, save_state);
2505
2506     GdipFree(ptf);
2507     return retval;
2508 }
2509
2510 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
2511 {
2512     INT save_state;
2513     GpStatus retval;
2514
2515     TRACE("(%p, %p, %p)\n", graphics, pen, path);
2516
2517     if(!pen || !graphics)
2518         return InvalidParameter;
2519
2520     if(graphics->busy)
2521         return ObjectBusy;
2522
2523     save_state = prepare_dc(graphics, pen);
2524
2525     retval = draw_poly(graphics, pen, path->pathdata.Points,
2526                        path->pathdata.Types, path->pathdata.Count, TRUE);
2527
2528     restore_dc(graphics, save_state);
2529
2530     return retval;
2531 }
2532
2533 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
2534     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2535 {
2536     INT save_state;
2537
2538     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
2539             width, height, startAngle, sweepAngle);
2540
2541     if(!graphics || !pen)
2542         return InvalidParameter;
2543
2544     if(graphics->busy)
2545         return ObjectBusy;
2546
2547     save_state = prepare_dc(graphics, pen);
2548     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2549
2550     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2551
2552     restore_dc(graphics, save_state);
2553
2554     return Ok;
2555 }
2556
2557 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
2558     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2559 {
2560     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
2561             width, height, startAngle, sweepAngle);
2562
2563     return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2564 }
2565
2566 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
2567     REAL y, REAL width, REAL height)
2568 {
2569     INT save_state;
2570     GpPointF ptf[4];
2571     POINT pti[4];
2572
2573     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
2574
2575     if(!pen || !graphics)
2576         return InvalidParameter;
2577
2578     if(graphics->busy)
2579         return ObjectBusy;
2580
2581     ptf[0].X = x;
2582     ptf[0].Y = y;
2583     ptf[1].X = x + width;
2584     ptf[1].Y = y;
2585     ptf[2].X = x + width;
2586     ptf[2].Y = y + height;
2587     ptf[3].X = x;
2588     ptf[3].Y = y + height;
2589
2590     save_state = prepare_dc(graphics, pen);
2591     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2592
2593     transform_and_round_points(graphics, pti, ptf, 4);
2594     Polygon(graphics->hdc, pti, 4);
2595
2596     restore_dc(graphics, save_state);
2597
2598     return Ok;
2599 }
2600
2601 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
2602     INT y, INT width, INT height)
2603 {
2604     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
2605
2606     return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2607 }
2608
2609 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
2610     GDIPCONST GpRectF* rects, INT count)
2611 {
2612     GpPointF *ptf;
2613     POINT *pti;
2614     INT save_state, i;
2615
2616     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2617
2618     if(!graphics || !pen || !rects || count < 1)
2619         return InvalidParameter;
2620
2621     if(graphics->busy)
2622         return ObjectBusy;
2623
2624     ptf = GdipAlloc(4 * count * sizeof(GpPointF));
2625     pti = GdipAlloc(4 * count * sizeof(POINT));
2626
2627     if(!ptf || !pti){
2628         GdipFree(ptf);
2629         GdipFree(pti);
2630         return OutOfMemory;
2631     }
2632
2633     for(i = 0; i < count; i++){
2634         ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
2635         ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
2636         ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
2637         ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
2638     }
2639
2640     save_state = prepare_dc(graphics, pen);
2641     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
2642
2643     transform_and_round_points(graphics, pti, ptf, 4 * count);
2644
2645     for(i = 0; i < count; i++)
2646         Polygon(graphics->hdc, &pti[4 * i], 4);
2647
2648     restore_dc(graphics, save_state);
2649
2650     GdipFree(ptf);
2651     GdipFree(pti);
2652
2653     return Ok;
2654 }
2655
2656 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
2657     GDIPCONST GpRect* rects, INT count)
2658 {
2659     GpRectF *rectsF;
2660     GpStatus ret;
2661     INT i;
2662
2663     TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
2664
2665     if(!rects || count<=0)
2666         return InvalidParameter;
2667
2668     rectsF = GdipAlloc(sizeof(GpRectF) * count);
2669     if(!rectsF)
2670         return OutOfMemory;
2671
2672     for(i = 0;i < count;i++){
2673         rectsF[i].X      = (REAL)rects[i].X;
2674         rectsF[i].Y      = (REAL)rects[i].Y;
2675         rectsF[i].Width  = (REAL)rects[i].Width;
2676         rectsF[i].Height = (REAL)rects[i].Height;
2677     }
2678
2679     ret = GdipDrawRectangles(graphics, pen, rectsF, count);
2680     GdipFree(rectsF);
2681
2682     return ret;
2683 }
2684
2685 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
2686     GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
2687 {
2688     GpPath *path;
2689     GpStatus stat;
2690
2691     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2692             count, tension, fill);
2693
2694     if(!graphics || !brush || !points)
2695         return InvalidParameter;
2696
2697     if(graphics->busy)
2698         return ObjectBusy;
2699
2700     if(count == 1)    /* Do nothing */
2701         return Ok;
2702
2703     stat = GdipCreatePath(fill, &path);
2704     if(stat != Ok)
2705         return stat;
2706
2707     stat = GdipAddPathClosedCurve2(path, points, count, tension);
2708     if(stat != Ok){
2709         GdipDeletePath(path);
2710         return stat;
2711     }
2712
2713     stat = GdipFillPath(graphics, brush, path);
2714     if(stat != Ok){
2715         GdipDeletePath(path);
2716         return stat;
2717     }
2718
2719     GdipDeletePath(path);
2720
2721     return Ok;
2722 }
2723
2724 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2725     GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2726 {
2727     GpPointF *ptf;
2728     GpStatus stat;
2729     INT i;
2730
2731     TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2732             count, tension, fill);
2733
2734     if(!points || count == 0)
2735         return InvalidParameter;
2736
2737     if(count == 1)    /* Do nothing */
2738         return Ok;
2739
2740     ptf = GdipAlloc(sizeof(GpPointF)*count);
2741     if(!ptf)
2742         return OutOfMemory;
2743
2744     for(i = 0;i < count;i++){
2745         ptf[i].X = (REAL)points[i].X;
2746         ptf[i].Y = (REAL)points[i].Y;
2747     }
2748
2749     stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2750
2751     GdipFree(ptf);
2752
2753     return stat;
2754 }
2755
2756 GpStatus WINGDIPAPI GdipFillClosedCurve(GpGraphics *graphics, GpBrush *brush,
2757     GDIPCONST GpPointF *points, INT count)
2758 {
2759     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2760     return GdipFillClosedCurve2(graphics, brush, points, count,
2761                0.5f, FillModeAlternate);
2762 }
2763
2764 GpStatus WINGDIPAPI GdipFillClosedCurveI(GpGraphics *graphics, GpBrush *brush,
2765     GDIPCONST GpPoint *points, INT count)
2766 {
2767     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2768     return GdipFillClosedCurve2I(graphics, brush, points, count,
2769                0.5f, FillModeAlternate);
2770 }
2771
2772 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2773     REAL y, REAL width, REAL height)
2774 {
2775     INT save_state;
2776     GpPointF ptf[2];
2777     POINT pti[2];
2778
2779     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2780
2781     if(!graphics || !brush)
2782         return InvalidParameter;
2783
2784     if(graphics->busy)
2785         return ObjectBusy;
2786
2787     if(!graphics->hdc)
2788     {
2789         FIXME("graphics object has no HDC\n");
2790         return Ok;
2791     }
2792
2793     ptf[0].X = x;
2794     ptf[0].Y = y;
2795     ptf[1].X = x + width;
2796     ptf[1].Y = y + height;
2797
2798     save_state = SaveDC(graphics->hdc);
2799     EndPath(graphics->hdc);
2800
2801     transform_and_round_points(graphics, pti, ptf, 2);
2802
2803     BeginPath(graphics->hdc);
2804     Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2805     EndPath(graphics->hdc);
2806
2807     brush_fill_path(graphics, brush);
2808
2809     RestoreDC(graphics->hdc, save_state);
2810
2811     return Ok;
2812 }
2813
2814 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2815     INT y, INT width, INT height)
2816 {
2817     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2818
2819     return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2820 }
2821
2822 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2823 {
2824     INT save_state;
2825     GpStatus retval;
2826
2827     TRACE("(%p, %p, %p)\n", graphics, brush, path);
2828
2829     if(!brush || !graphics || !path)
2830         return InvalidParameter;
2831
2832     if(graphics->busy)
2833         return ObjectBusy;
2834
2835     if(!graphics->hdc)
2836     {
2837         FIXME("graphics object has no HDC\n");
2838         return Ok;
2839     }
2840
2841     save_state = SaveDC(graphics->hdc);
2842     EndPath(graphics->hdc);
2843     SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2844                                                                     : WINDING));
2845
2846     BeginPath(graphics->hdc);
2847     retval = draw_poly(graphics, NULL, path->pathdata.Points,
2848                        path->pathdata.Types, path->pathdata.Count, FALSE);
2849
2850     if(retval != Ok)
2851         goto end;
2852
2853     EndPath(graphics->hdc);
2854     brush_fill_path(graphics, brush);
2855
2856     retval = Ok;
2857
2858 end:
2859     RestoreDC(graphics->hdc, save_state);
2860
2861     return retval;
2862 }
2863
2864 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2865     REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2866 {
2867     INT save_state;
2868
2869     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2870             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2871
2872     if(!graphics || !brush)
2873         return InvalidParameter;
2874
2875     if(graphics->busy)
2876         return ObjectBusy;
2877
2878     if(!graphics->hdc)
2879     {
2880         FIXME("graphics object has no HDC\n");
2881         return Ok;
2882     }
2883
2884     save_state = SaveDC(graphics->hdc);
2885     EndPath(graphics->hdc);
2886
2887     BeginPath(graphics->hdc);
2888     draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2889     EndPath(graphics->hdc);
2890
2891     brush_fill_path(graphics, brush);
2892
2893     RestoreDC(graphics->hdc, save_state);
2894
2895     return Ok;
2896 }
2897
2898 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2899     INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2900 {
2901     TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2902             graphics, brush, x, y, width, height, startAngle, sweepAngle);
2903
2904     return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2905 }
2906
2907 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2908     GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2909 {
2910     INT save_state;
2911     GpPointF *ptf = NULL;
2912     POINT *pti = NULL;
2913     GpStatus retval = Ok;
2914
2915     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2916
2917     if(!graphics || !brush || !points || !count)
2918         return InvalidParameter;
2919
2920     if(graphics->busy)
2921         return ObjectBusy;
2922
2923     if(!graphics->hdc)
2924     {
2925         FIXME("graphics object has no HDC\n");
2926         return Ok;
2927     }
2928
2929     ptf = GdipAlloc(count * sizeof(GpPointF));
2930     pti = GdipAlloc(count * sizeof(POINT));
2931     if(!ptf || !pti){
2932         retval = OutOfMemory;
2933         goto end;
2934     }
2935
2936     memcpy(ptf, points, count * sizeof(GpPointF));
2937
2938     save_state = SaveDC(graphics->hdc);
2939     EndPath(graphics->hdc);
2940     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2941                                                                   : WINDING));
2942
2943     transform_and_round_points(graphics, pti, ptf, count);
2944
2945     BeginPath(graphics->hdc);
2946     Polygon(graphics->hdc, pti, count);
2947     EndPath(graphics->hdc);
2948
2949     brush_fill_path(graphics, brush);
2950
2951     RestoreDC(graphics->hdc, save_state);
2952
2953 end:
2954     GdipFree(ptf);
2955     GdipFree(pti);
2956
2957     return retval;
2958 }
2959
2960 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2961     GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2962 {
2963     INT save_state, i;
2964     GpPointF *ptf = NULL;
2965     POINT *pti = NULL;
2966     GpStatus retval = Ok;
2967
2968     TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2969
2970     if(!graphics || !brush || !points || !count)
2971         return InvalidParameter;
2972
2973     if(graphics->busy)
2974         return ObjectBusy;
2975
2976     if(!graphics->hdc)
2977     {
2978         FIXME("graphics object has no HDC\n");
2979         return Ok;
2980     }
2981
2982     ptf = GdipAlloc(count * sizeof(GpPointF));
2983     pti = GdipAlloc(count * sizeof(POINT));
2984     if(!ptf || !pti){
2985         retval = OutOfMemory;
2986         goto end;
2987     }
2988
2989     for(i = 0; i < count; i ++){
2990         ptf[i].X = (REAL) points[i].X;
2991         ptf[i].Y = (REAL) points[i].Y;
2992     }
2993
2994     save_state = SaveDC(graphics->hdc);
2995     EndPath(graphics->hdc);
2996     SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2997                                                                   : WINDING));
2998
2999     transform_and_round_points(graphics, pti, ptf, count);
3000
3001     BeginPath(graphics->hdc);
3002     Polygon(graphics->hdc, pti, count);
3003     EndPath(graphics->hdc);
3004
3005     brush_fill_path(graphics, brush);
3006
3007     RestoreDC(graphics->hdc, save_state);
3008
3009 end:
3010     GdipFree(ptf);
3011     GdipFree(pti);
3012
3013     return retval;
3014 }
3015
3016 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
3017     GDIPCONST GpPointF *points, INT count)
3018 {
3019     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3020
3021     return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
3022 }
3023
3024 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
3025     GDIPCONST GpPoint *points, INT count)
3026 {
3027     TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
3028
3029     return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
3030 }
3031
3032 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
3033     REAL x, REAL y, REAL width, REAL height)
3034 {
3035     INT save_state;
3036     GpPointF ptf[4];
3037     POINT pti[4];
3038
3039     TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
3040
3041     if(!graphics || !brush)
3042         return InvalidParameter;
3043
3044     if(graphics->busy)
3045         return ObjectBusy;
3046
3047     if(!graphics->hdc)
3048     {
3049         FIXME("graphics object has no HDC\n");
3050         return Ok;
3051     }
3052
3053     ptf[0].X = x;
3054     ptf[0].Y = y;
3055     ptf[1].X = x + width;
3056     ptf[1].Y = y;
3057     ptf[2].X = x + width;
3058     ptf[2].Y = y + height;
3059     ptf[3].X = x;
3060     ptf[3].Y = y + height;
3061
3062     save_state = SaveDC(graphics->hdc);
3063     EndPath(graphics->hdc);
3064
3065     transform_and_round_points(graphics, pti, ptf, 4);
3066
3067     BeginPath(graphics->hdc);
3068     Polygon(graphics->hdc, pti, 4);
3069     EndPath(graphics->hdc);
3070
3071     brush_fill_path(graphics, brush);
3072
3073     RestoreDC(graphics->hdc, save_state);
3074
3075     return Ok;
3076 }
3077
3078 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
3079     INT x, INT y, INT width, INT height)
3080 {
3081     INT save_state;
3082     GpPointF ptf[4];
3083     POINT pti[4];
3084
3085     TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
3086
3087     if(!graphics || !brush)
3088         return InvalidParameter;
3089
3090     if(graphics->busy)
3091         return ObjectBusy;
3092
3093     if(!graphics->hdc)
3094     {
3095         FIXME("graphics object has no HDC\n");
3096         return Ok;
3097     }
3098
3099     ptf[0].X = x;
3100     ptf[0].Y = y;
3101     ptf[1].X = x + width;
3102     ptf[1].Y = y;
3103     ptf[2].X = x + width;
3104     ptf[2].Y = y + height;
3105     ptf[3].X = x;
3106     ptf[3].Y = y + height;
3107
3108     save_state = SaveDC(graphics->hdc);
3109     EndPath(graphics->hdc);
3110
3111     transform_and_round_points(graphics, pti, ptf, 4);
3112
3113     BeginPath(graphics->hdc);
3114     Polygon(graphics->hdc, pti, 4);
3115     EndPath(graphics->hdc);
3116
3117     brush_fill_path(graphics, brush);
3118
3119     RestoreDC(graphics->hdc, save_state);
3120
3121     return Ok;
3122 }
3123
3124 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
3125     INT count)
3126 {
3127     GpStatus ret;
3128     INT i;
3129
3130     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3131
3132     if(!rects)
3133         return InvalidParameter;
3134
3135     for(i = 0; i < count; i++){
3136         ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
3137         if(ret != Ok)   return ret;
3138     }
3139
3140     return Ok;
3141 }
3142
3143 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
3144     INT count)
3145 {
3146     GpRectF *rectsF;
3147     GpStatus ret;
3148     INT i;
3149
3150     TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
3151
3152     if(!rects || count <= 0)
3153         return InvalidParameter;
3154
3155     rectsF = GdipAlloc(sizeof(GpRectF)*count);
3156     if(!rectsF)
3157         return OutOfMemory;
3158
3159     for(i = 0; i < count; i++){
3160         rectsF[i].X      = (REAL)rects[i].X;
3161         rectsF[i].Y      = (REAL)rects[i].Y;
3162         rectsF[i].X      = (REAL)rects[i].Width;
3163         rectsF[i].Height = (REAL)rects[i].Height;
3164     }
3165
3166     ret = GdipFillRectangles(graphics,brush,rectsF,count);
3167     GdipFree(rectsF);
3168
3169     return ret;
3170 }
3171
3172 /*****************************************************************************
3173  * GdipFillRegion [GDIPLUS.@]
3174  */
3175 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
3176         GpRegion* region)
3177 {
3178     INT save_state;
3179     GpStatus status;
3180     HRGN hrgn;
3181     RECT rc;
3182
3183     TRACE("(%p, %p, %p)\n", graphics, brush, region);
3184
3185     if (!(graphics && brush && region))
3186         return InvalidParameter;
3187
3188     if(graphics->busy)
3189         return ObjectBusy;
3190
3191     if(!graphics->hdc)
3192     {
3193         FIXME("graphics object has no HDC\n");
3194         return Ok;
3195     }
3196
3197     status = GdipGetRegionHRgn(region, graphics, &hrgn);
3198     if(status != Ok)
3199         return status;
3200
3201     save_state = SaveDC(graphics->hdc);
3202     EndPath(graphics->hdc);
3203
3204     ExtSelectClipRgn(graphics->hdc, hrgn, RGN_AND);
3205
3206     if (GetClipBox(graphics->hdc, &rc) != NULLREGION)
3207     {
3208         BeginPath(graphics->hdc);
3209         Rectangle(graphics->hdc, rc.left, rc.top, rc.right, rc.bottom);
3210         EndPath(graphics->hdc);
3211
3212         brush_fill_path(graphics, brush);
3213     }
3214
3215     RestoreDC(graphics->hdc, save_state);
3216
3217     DeleteObject(hrgn);
3218
3219     return Ok;
3220 }
3221
3222 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
3223 {
3224     TRACE("(%p,%u)\n", graphics, intention);
3225
3226     if(!graphics)
3227         return InvalidParameter;
3228
3229     if(graphics->busy)
3230         return ObjectBusy;
3231
3232     /* We have no internal operation queue, so there's no need to clear it. */
3233
3234     if (graphics->hdc)
3235         GdiFlush();
3236
3237     return Ok;
3238 }
3239
3240 /*****************************************************************************
3241  * GdipGetClipBounds [GDIPLUS.@]
3242  */
3243 GpStatus WINGDIPAPI GdipGetClipBounds(GpGraphics *graphics, GpRectF *rect)
3244 {
3245     TRACE("(%p, %p)\n", graphics, rect);
3246
3247     if(!graphics)
3248         return InvalidParameter;
3249
3250     if(graphics->busy)
3251         return ObjectBusy;
3252
3253     return GdipGetRegionBounds(graphics->clip, graphics, rect);
3254 }
3255
3256 /*****************************************************************************
3257  * GdipGetClipBoundsI [GDIPLUS.@]
3258  */
3259 GpStatus WINGDIPAPI GdipGetClipBoundsI(GpGraphics *graphics, GpRect *rect)
3260 {
3261     TRACE("(%p, %p)\n", graphics, rect);
3262
3263     if(!graphics)
3264         return InvalidParameter;
3265
3266     if(graphics->busy)
3267         return ObjectBusy;
3268
3269     return GdipGetRegionBoundsI(graphics->clip, graphics, rect);
3270 }
3271
3272 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
3273 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
3274     CompositingMode *mode)
3275 {
3276     TRACE("(%p, %p)\n", graphics, mode);
3277
3278     if(!graphics || !mode)
3279         return InvalidParameter;
3280
3281     if(graphics->busy)
3282         return ObjectBusy;
3283
3284     *mode = graphics->compmode;
3285
3286     return Ok;
3287 }
3288
3289 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
3290 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
3291     CompositingQuality *quality)
3292 {
3293     TRACE("(%p, %p)\n", graphics, quality);
3294
3295     if(!graphics || !quality)
3296         return InvalidParameter;
3297
3298     if(graphics->busy)
3299         return ObjectBusy;
3300
3301     *quality = graphics->compqual;
3302
3303     return Ok;
3304 }
3305
3306 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
3307 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
3308     InterpolationMode *mode)
3309 {
3310     TRACE("(%p, %p)\n", graphics, mode);
3311
3312     if(!graphics || !mode)
3313         return InvalidParameter;
3314
3315     if(graphics->busy)
3316         return ObjectBusy;
3317
3318     *mode = graphics->interpolation;
3319
3320     return Ok;
3321 }
3322
3323 /* FIXME: Need to handle color depths less than 24bpp */
3324 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
3325 {
3326     FIXME("(%p, %p): Passing color unmodified\n", graphics, argb);
3327
3328     if(!graphics || !argb)
3329         return InvalidParameter;
3330
3331     if(graphics->busy)
3332         return ObjectBusy;
3333
3334     return Ok;
3335 }
3336
3337 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
3338 {
3339     TRACE("(%p, %p)\n", graphics, scale);
3340
3341     if(!graphics || !scale)
3342         return InvalidParameter;
3343
3344     if(graphics->busy)
3345         return ObjectBusy;
3346
3347     *scale = graphics->scale;
3348
3349     return Ok;
3350 }
3351
3352 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
3353 {
3354     TRACE("(%p, %p)\n", graphics, unit);
3355
3356     if(!graphics || !unit)
3357         return InvalidParameter;
3358
3359     if(graphics->busy)
3360         return ObjectBusy;
3361
3362     *unit = graphics->unit;
3363
3364     return Ok;
3365 }
3366
3367 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
3368 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
3369     *mode)
3370 {
3371     TRACE("(%p, %p)\n", graphics, mode);
3372
3373     if(!graphics || !mode)
3374         return InvalidParameter;
3375
3376     if(graphics->busy)
3377         return ObjectBusy;
3378
3379     *mode = graphics->pixeloffset;
3380
3381     return Ok;
3382 }
3383
3384 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
3385 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
3386 {
3387     TRACE("(%p, %p)\n", graphics, mode);
3388
3389     if(!graphics || !mode)
3390         return InvalidParameter;
3391
3392     if(graphics->busy)
3393         return ObjectBusy;
3394
3395     *mode = graphics->smoothing;
3396
3397     return Ok;
3398 }
3399
3400 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
3401 {
3402     TRACE("(%p, %p)\n", graphics, contrast);
3403
3404     if(!graphics || !contrast)
3405         return InvalidParameter;
3406
3407     *contrast = graphics->textcontrast;
3408
3409     return Ok;
3410 }
3411
3412 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
3413 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
3414     TextRenderingHint *hint)
3415 {
3416     TRACE("(%p, %p)\n", graphics, hint);
3417
3418     if(!graphics || !hint)
3419         return InvalidParameter;
3420
3421     if(graphics->busy)
3422         return ObjectBusy;
3423
3424     *hint = graphics->texthint;
3425
3426     return Ok;
3427 }
3428
3429 GpStatus WINGDIPAPI GdipGetVisibleClipBounds(GpGraphics *graphics, GpRectF *rect)
3430 {
3431     GpRegion *clip_rgn;
3432     GpStatus stat;
3433
3434     TRACE("(%p, %p)\n", graphics, rect);
3435
3436     if(!graphics || !rect)
3437         return InvalidParameter;
3438
3439     if(graphics->busy)
3440         return ObjectBusy;
3441
3442     /* intersect window and graphics clipping regions */
3443     if((stat = GdipCreateRegion(&clip_rgn)) != Ok)
3444         return stat;
3445
3446     if((stat = get_visible_clip_region(graphics, clip_rgn)) != Ok)
3447         goto cleanup;
3448
3449     /* get bounds of the region */
3450     stat = GdipGetRegionBounds(clip_rgn, graphics, rect);
3451
3452 cleanup:
3453     GdipDeleteRegion(clip_rgn);
3454
3455     return stat;
3456 }
3457
3458 GpStatus WINGDIPAPI GdipGetVisibleClipBoundsI(GpGraphics *graphics, GpRect *rect)
3459 {
3460     GpRectF rectf;
3461     GpStatus stat;
3462
3463     TRACE("(%p, %p)\n", graphics, rect);
3464
3465     if(!graphics || !rect)
3466         return InvalidParameter;
3467
3468     if((stat = GdipGetVisibleClipBounds(graphics, &rectf)) == Ok)
3469     {
3470         rect->X = roundr(rectf.X);
3471         rect->Y = roundr(rectf.Y);
3472         rect->Width  = roundr(rectf.Width);
3473         rect->Height = roundr(rectf.Height);
3474     }
3475
3476     return stat;
3477 }
3478
3479 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3480 {
3481     TRACE("(%p, %p)\n", graphics, matrix);
3482
3483     if(!graphics || !matrix)
3484         return InvalidParameter;
3485
3486     if(graphics->busy)
3487         return ObjectBusy;
3488
3489     *matrix = *graphics->worldtrans;
3490     return Ok;
3491 }
3492
3493 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
3494 {
3495     GpSolidFill *brush;
3496     GpStatus stat;
3497     GpRectF wnd_rect;
3498
3499     TRACE("(%p, %x)\n", graphics, color);
3500
3501     if(!graphics)
3502         return InvalidParameter;
3503
3504     if(graphics->busy)
3505         return ObjectBusy;
3506
3507     if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
3508         return stat;
3509
3510     if((stat = get_graphics_bounds(graphics, &wnd_rect)) != Ok){
3511         GdipDeleteBrush((GpBrush*)brush);
3512         return stat;
3513     }
3514
3515     GdipFillRectangle(graphics, (GpBrush*)brush, wnd_rect.X, wnd_rect.Y,
3516                                                  wnd_rect.Width, wnd_rect.Height);
3517
3518     GdipDeleteBrush((GpBrush*)brush);
3519
3520     return Ok;
3521 }
3522
3523 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
3524 {
3525     TRACE("(%p, %p)\n", graphics, res);
3526
3527     if(!graphics || !res)
3528         return InvalidParameter;
3529
3530     return GdipIsEmptyRegion(graphics->clip, graphics, res);
3531 }
3532
3533 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
3534 {
3535     GpStatus stat;
3536     GpRegion* rgn;
3537     GpPointF pt;
3538
3539     TRACE("(%p, %.2f, %.2f, %p)\n", graphics, x, y, result);
3540
3541     if(!graphics || !result)
3542         return InvalidParameter;
3543
3544     if(graphics->busy)
3545         return ObjectBusy;
3546
3547     pt.X = x;
3548     pt.Y = y;
3549     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3550                    CoordinateSpaceWorld, &pt, 1)) != Ok)
3551         return stat;
3552
3553     if((stat = GdipCreateRegion(&rgn)) != Ok)
3554         return stat;
3555
3556     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3557         goto cleanup;
3558
3559     stat = GdipIsVisibleRegionPoint(rgn, pt.X, pt.Y, graphics, result);
3560
3561 cleanup:
3562     GdipDeleteRegion(rgn);
3563     return stat;
3564 }
3565
3566 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
3567 {
3568     return GdipIsVisiblePoint(graphics, (REAL)x, (REAL)y, result);
3569 }
3570
3571 GpStatus WINGDIPAPI GdipIsVisibleRect(GpGraphics *graphics, REAL x, REAL y, REAL width, REAL height, BOOL *result)
3572 {
3573     GpStatus stat;
3574     GpRegion* rgn;
3575     GpPointF pts[2];
3576
3577     TRACE("(%p %.2f %.2f %.2f %.2f %p)\n", graphics, x, y, width, height, result);
3578
3579     if(!graphics || !result)
3580         return InvalidParameter;
3581
3582     if(graphics->busy)
3583         return ObjectBusy;
3584
3585     pts[0].X = x;
3586     pts[0].Y = y;
3587     pts[1].X = x + width;
3588     pts[1].Y = y + height;
3589
3590     if((stat = GdipTransformPoints(graphics, CoordinateSpaceDevice,
3591                     CoordinateSpaceWorld, pts, 2)) != Ok)
3592         return stat;
3593
3594     pts[1].X -= pts[0].X;
3595     pts[1].Y -= pts[0].Y;
3596
3597     if((stat = GdipCreateRegion(&rgn)) != Ok)
3598         return stat;
3599
3600     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
3601         goto cleanup;
3602
3603     stat = GdipIsVisibleRegionRect(rgn, pts[0].X, pts[0].Y, pts[1].X, pts[1].Y, graphics, result);
3604
3605 cleanup:
3606     GdipDeleteRegion(rgn);
3607     return stat;
3608 }
3609
3610 GpStatus WINGDIPAPI GdipIsVisibleRectI(GpGraphics *graphics, INT x, INT y, INT width, INT height, BOOL *result)
3611 {
3612     return GdipIsVisibleRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, result);
3613 }
3614
3615 typedef GpStatus (*gdip_format_string_callback)(GpGraphics *graphics,
3616     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3617     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3618     INT lineno, const RectF *bounds, void *user_data);
3619
3620 static GpStatus gdip_format_string(GpGraphics *graphics,
3621     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3622     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3623     gdip_format_string_callback callback, void *user_data)
3624 {
3625     WCHAR* stringdup;
3626     int sum = 0, height = 0, fit, fitcpy, i, j, lret, nwidth,
3627         nheight, lineend, lineno = 0;
3628     RectF bounds;
3629     StringAlignment halign;
3630     GpStatus stat = Ok;
3631     SIZE size;
3632
3633     if(length == -1) length = lstrlenW(string);
3634
3635     stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
3636     if(!stringdup) return OutOfMemory;
3637
3638     nwidth = roundr(rect->Width);
3639     nheight = roundr(rect->Height);
3640
3641     if (rect->Width >= INT_MAX || rect->Width < 0.5) nwidth = INT_MAX;
3642     if (rect->Height >= INT_MAX || rect->Width < 0.5) nheight = INT_MAX;
3643
3644     for(i = 0, j = 0; i < length; i++){
3645         /* FIXME: This makes the indexes passed to callback inaccurate. */
3646         if(!isprintW(string[i]) && (string[i] != '\n'))
3647             continue;
3648
3649         stringdup[j] = string[i];
3650         j++;
3651     }
3652
3653     length = j;
3654
3655     if (format) halign = format->align;
3656     else halign = StringAlignmentNear;
3657
3658     while(sum < length){
3659         GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
3660                               nwidth, &fit, NULL, &size);
3661         fitcpy = fit;
3662
3663         if(fit == 0)
3664             break;
3665
3666         for(lret = 0; lret < fit; lret++)
3667             if(*(stringdup + sum + lret) == '\n')
3668                 break;
3669
3670         /* Line break code (may look strange, but it imitates windows). */
3671         if(lret < fit)
3672             lineend = fit = lret;    /* this is not an off-by-one error */
3673         else if(fit < (length - sum)){
3674             if(*(stringdup + sum + fit) == ' ')
3675                 while(*(stringdup + sum + fit) == ' ')
3676                     fit++;
3677             else
3678                 while(*(stringdup + sum + fit - 1) != ' '){
3679                     fit--;
3680
3681                     if(*(stringdup + sum + fit) == '\t')
3682                         break;
3683
3684                     if(fit == 0){
3685                         fit = fitcpy;
3686                         break;
3687                     }
3688                 }
3689             lineend = fit;
3690             while(*(stringdup + sum + lineend - 1) == ' ' ||
3691                   *(stringdup + sum + lineend - 1) == '\t')
3692                 lineend--;
3693         }
3694         else
3695             lineend = fit;
3696
3697         GetTextExtentExPointW(graphics->hdc, stringdup + sum, lineend,
3698                               nwidth, &j, NULL, &size);
3699
3700         bounds.Width = size.cx;
3701
3702         if(height + size.cy > nheight)
3703             bounds.Height = nheight - (height + size.cy);
3704         else
3705             bounds.Height = size.cy;
3706
3707         bounds.Y = rect->Y + height;
3708
3709         switch (halign)
3710         {
3711         case StringAlignmentNear:
3712         default:
3713             bounds.X = rect->X;
3714             break;
3715         case StringAlignmentCenter:
3716             bounds.X = rect->X + (rect->Width/2) - (bounds.Width/2);
3717             break;
3718         case StringAlignmentFar:
3719             bounds.X = rect->X + rect->Width - bounds.Width;
3720             break;
3721         }
3722
3723         stat = callback(graphics, stringdup, sum, lineend,
3724             font, rect, format, lineno, &bounds, user_data);
3725
3726         if (stat != Ok)
3727             break;
3728
3729         sum += fit + (lret < fitcpy ? 1 : 0);
3730         height += size.cy;
3731         lineno++;
3732
3733         if(height > nheight)
3734             break;
3735
3736         /* Stop if this was a linewrap (but not if it was a linebreak). */
3737         if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
3738             break;
3739     }
3740
3741     GdipFree(stringdup);
3742
3743     return stat;
3744 }
3745
3746 struct measure_ranges_args {
3747     GpRegion **regions;
3748 };
3749
3750 static GpStatus measure_ranges_callback(GpGraphics *graphics,
3751     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3752     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3753     INT lineno, const RectF *bounds, void *user_data)
3754 {
3755     int i;
3756     GpStatus stat = Ok;
3757     struct measure_ranges_args *args = user_data;
3758
3759     for (i=0; i<format->range_count; i++)
3760     {
3761         INT range_start = max(index, format->character_ranges[i].First);
3762         INT range_end = min(index+length, format->character_ranges[i].First+format->character_ranges[i].Length);
3763         if (range_start < range_end)
3764         {
3765             GpRectF range_rect;
3766             SIZE range_size;
3767
3768             range_rect.Y = bounds->Y;
3769             range_rect.Height = bounds->Height;
3770
3771             GetTextExtentExPointW(graphics->hdc, string + index, range_start - index,
3772                                   INT_MAX, NULL, NULL, &range_size);
3773             range_rect.X = bounds->X + range_size.cx;
3774
3775             GetTextExtentExPointW(graphics->hdc, string + index, range_end - index,
3776                                   INT_MAX, NULL, NULL, &range_size);
3777             range_rect.Width = (bounds->X + range_size.cx) - range_rect.X;
3778
3779             stat = GdipCombineRegionRect(args->regions[i], &range_rect, CombineModeUnion);
3780             if (stat != Ok)
3781                 break;
3782         }
3783     }
3784
3785     return stat;
3786 }
3787
3788 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
3789         GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
3790         GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
3791         INT regionCount, GpRegion** regions)
3792 {
3793     GpStatus stat;
3794     int i;
3795     HFONT oldfont;
3796     struct measure_ranges_args args;
3797
3798     TRACE("(%p %s %d %p %s %p %d %p)\n", graphics, debugstr_w(string),
3799             length, font, debugstr_rectf(layoutRect), stringFormat, regionCount, regions);
3800
3801     if (!(graphics && string && font && layoutRect && stringFormat && regions))
3802         return InvalidParameter;
3803
3804     if (regionCount < stringFormat->range_count)
3805         return InvalidParameter;
3806
3807     if(!graphics->hdc)
3808     {
3809         FIXME("graphics object has no HDC\n");
3810         return NotImplemented;
3811     }
3812
3813     if (stringFormat->attr)
3814         TRACE("may be ignoring some format flags: attr %x\n", stringFormat->attr);
3815
3816     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3817
3818     for (i=0; i<stringFormat->range_count; i++)
3819     {
3820         stat = GdipSetEmpty(regions[i]);
3821         if (stat != Ok)
3822             return stat;
3823     }
3824
3825     args.regions = regions;
3826
3827     stat = gdip_format_string(graphics, string, length, font, layoutRect, stringFormat,
3828         measure_ranges_callback, &args);
3829
3830     DeleteObject(SelectObject(graphics->hdc, oldfont));
3831
3832     return stat;
3833 }
3834
3835 struct measure_string_args {
3836     RectF *bounds;
3837     INT *codepointsfitted;
3838     INT *linesfilled;
3839 };
3840
3841 static GpStatus measure_string_callback(GpGraphics *graphics,
3842     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3843     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3844     INT lineno, const RectF *bounds, void *user_data)
3845 {
3846     struct measure_string_args *args = user_data;
3847
3848     if (bounds->Width > args->bounds->Width)
3849         args->bounds->Width = bounds->Width;
3850
3851     if (bounds->Height + bounds->Y > args->bounds->Height + args->bounds->Y)
3852         args->bounds->Height = bounds->Height + bounds->Y - args->bounds->Y;
3853
3854     if (args->codepointsfitted)
3855         *args->codepointsfitted = index + length;
3856
3857     if (args->linesfilled)
3858         (*args->linesfilled)++;
3859
3860     return Ok;
3861 }
3862
3863 /* Find the smallest rectangle that bounds the text when it is printed in rect
3864  * according to the format options listed in format. If rect has 0 width and
3865  * height, then just find the smallest rectangle that bounds the text when it's
3866  * printed at location (rect->X, rect-Y). */
3867 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
3868     GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
3869     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
3870     INT *codepointsfitted, INT *linesfilled)
3871 {
3872     HFONT oldfont;
3873     struct measure_string_args args;
3874
3875     TRACE("(%p, %s, %i, %p, %s, %p, %p, %p, %p)\n", graphics,
3876         debugstr_wn(string, length), length, font, debugstr_rectf(rect), format,
3877         bounds, codepointsfitted, linesfilled);
3878
3879     if(!graphics || !string || !font || !rect || !bounds)
3880         return InvalidParameter;
3881
3882     if(!graphics->hdc)
3883     {
3884         FIXME("graphics object has no HDC\n");
3885         return NotImplemented;
3886     }
3887
3888     if(linesfilled) *linesfilled = 0;
3889     if(codepointsfitted) *codepointsfitted = 0;
3890
3891     if(format)
3892         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3893
3894     oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
3895
3896     bounds->X = rect->X;
3897     bounds->Y = rect->Y;
3898     bounds->Width = 0.0;
3899     bounds->Height = 0.0;
3900
3901     args.bounds = bounds;
3902     args.codepointsfitted = codepointsfitted;
3903     args.linesfilled = linesfilled;
3904
3905     gdip_format_string(graphics, string, length, font, rect, format,
3906         measure_string_callback, &args);
3907
3908     DeleteObject(SelectObject(graphics->hdc, oldfont));
3909
3910     return Ok;
3911 }
3912
3913 struct draw_string_args {
3914     POINT drawbase;
3915     UINT drawflags;
3916     REAL ang_cos, ang_sin;
3917 };
3918
3919 static GpStatus draw_string_callback(GpGraphics *graphics,
3920     GDIPCONST WCHAR *string, INT index, INT length, GDIPCONST GpFont *font,
3921     GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format,
3922     INT lineno, const RectF *bounds, void *user_data)
3923 {
3924     struct draw_string_args *args = user_data;
3925     RECT drawcoord;
3926
3927     drawcoord.left = drawcoord.right = args->drawbase.x + roundr(args->ang_sin * bounds->Y);
3928     drawcoord.top = drawcoord.bottom = args->drawbase.y + roundr(args->ang_cos * bounds->Y);
3929
3930     DrawTextW(graphics->hdc, string + index, length, &drawcoord, args->drawflags);
3931
3932     return Ok;
3933 }
3934
3935 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
3936     INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
3937     GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
3938 {
3939     HRGN rgn = NULL;
3940     HFONT gdifont;
3941     LOGFONTW lfw;
3942     TEXTMETRICW textmet;
3943     GpPointF pt[3], rectcpy[4];
3944     POINT corners[4];
3945     REAL angle, rel_width, rel_height;
3946     INT offsety = 0, save_state;
3947     struct draw_string_args args;
3948     RectF scaled_rect;
3949
3950     TRACE("(%p, %s, %i, %p, %s, %p, %p)\n", graphics, debugstr_wn(string, length),
3951         length, font, debugstr_rectf(rect), format, brush);
3952
3953     if(!graphics || !string || !font || !brush || !rect)
3954         return InvalidParameter;
3955
3956     if((brush->bt != BrushTypeSolidColor)){
3957         FIXME("not implemented for given parameters\n");
3958         return NotImplemented;
3959     }
3960
3961     if(!graphics->hdc)
3962     {
3963         FIXME("graphics object has no HDC\n");
3964         return NotImplemented;
3965     }
3966
3967     if(format){
3968         TRACE("may be ignoring some format flags: attr %x\n", format->attr);
3969
3970         /* Should be no need to explicitly test for StringAlignmentNear as
3971          * that is default behavior if no alignment is passed. */
3972         if(format->vertalign != StringAlignmentNear){
3973             RectF bounds;
3974             GdipMeasureString(graphics, string, length, font, rect, format, &bounds, 0, 0);
3975
3976             if(format->vertalign == StringAlignmentCenter)
3977                 offsety = (rect->Height - bounds.Height) / 2;
3978             else if(format->vertalign == StringAlignmentFar)
3979                 offsety = (rect->Height - bounds.Height);
3980         }
3981     }
3982
3983     save_state = SaveDC(graphics->hdc);
3984     SetBkMode(graphics->hdc, TRANSPARENT);
3985     SetTextColor(graphics->hdc, brush->lb.lbColor);
3986
3987     pt[0].X = 0.0;
3988     pt[0].Y = 0.0;
3989     pt[1].X = 1.0;
3990     pt[1].Y = 0.0;
3991     pt[2].X = 0.0;
3992     pt[2].Y = 1.0;
3993     GdipTransformPoints(graphics, CoordinateSpaceDevice, CoordinateSpaceWorld, pt, 3);
3994     angle = -gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
3995     args.ang_cos = cos(angle);
3996     args.ang_sin = sin(angle);
3997     rel_width = sqrt((pt[1].Y-pt[0].Y)*(pt[1].Y-pt[0].Y)+
3998                      (pt[1].X-pt[0].X)*(pt[1].X-pt[0].X));
3999     rel_height = sqrt((pt[2].Y-pt[0].Y)*(pt[2].Y-pt[0].Y)+
4000                       (pt[2].X-pt[0].X)*(pt[2].X-pt[0].X));
4001
4002     rectcpy[3].X = rectcpy[0].X = rect->X;
4003     rectcpy[1].Y = rectcpy[0].Y = rect->Y + offsety;
4004     rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
4005     rectcpy[3].Y = rectcpy[2].Y = rect->Y + offsety + rect->Height;
4006     transform_and_round_points(graphics, corners, rectcpy, 4);
4007
4008     scaled_rect.X = 0.0;
4009     scaled_rect.Y = 0.0;
4010     scaled_rect.Width = rel_width * rect->Width;
4011     scaled_rect.Height = rel_height * rect->Height;
4012
4013     if (roundr(scaled_rect.Width) != 0 && roundr(scaled_rect.Height) != 0)
4014     {
4015         /* FIXME: If only the width or only the height is 0, we should probably still clip */
4016         rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
4017         SelectClipRgn(graphics->hdc, rgn);
4018     }
4019
4020     /* Use gdi to find the font, then perform transformations on it (height,
4021      * width, angle). */
4022     SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
4023     GetTextMetricsW(graphics->hdc, &textmet);
4024     lfw = font->lfw;
4025
4026     lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
4027     lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
4028
4029     lfw.lfEscapement = lfw.lfOrientation = roundr((angle / M_PI) * 1800.0);
4030
4031     gdifont = CreateFontIndirectW(&lfw);
4032     DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
4033
4034     if (!format || format->align == StringAlignmentNear)
4035     {
4036         args.drawbase.x = corners[0].x;
4037         args.drawbase.y = corners[0].y;
4038         args.drawflags = DT_NOCLIP | DT_EXPANDTABS;
4039     }
4040     else if (format->align == StringAlignmentCenter)
4041     {
4042         args.drawbase.x = (corners[0].x + corners[1].x)/2;
4043         args.drawbase.y = (corners[0].y + corners[1].y)/2;
4044         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_CENTER;
4045     }
4046     else /* (format->align == StringAlignmentFar) */
4047     {
4048         args.drawbase.x = corners[1].x;
4049         args.drawbase.y = corners[1].y;
4050         args.drawflags = DT_NOCLIP | DT_EXPANDTABS | DT_RIGHT;
4051     }
4052
4053     gdip_format_string(graphics, string, length, font, &scaled_rect, format,
4054         draw_string_callback, &args);
4055
4056     DeleteObject(rgn);
4057     DeleteObject(gdifont);
4058
4059     RestoreDC(graphics->hdc, save_state);
4060
4061     return Ok;
4062 }
4063
4064 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
4065 {
4066     TRACE("(%p)\n", graphics);
4067
4068     if(!graphics)
4069         return InvalidParameter;
4070
4071     if(graphics->busy)
4072         return ObjectBusy;
4073
4074     return GdipSetInfinite(graphics->clip);
4075 }
4076
4077 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
4078 {
4079     TRACE("(%p)\n", graphics);
4080
4081     if(!graphics)
4082         return InvalidParameter;
4083
4084     if(graphics->busy)
4085         return ObjectBusy;
4086
4087     graphics->worldtrans->matrix[0] = 1.0;
4088     graphics->worldtrans->matrix[1] = 0.0;
4089     graphics->worldtrans->matrix[2] = 0.0;
4090     graphics->worldtrans->matrix[3] = 1.0;
4091     graphics->worldtrans->matrix[4] = 0.0;
4092     graphics->worldtrans->matrix[5] = 0.0;
4093
4094     return Ok;
4095 }
4096
4097 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
4098 {
4099     return GdipEndContainer(graphics, state);
4100 }
4101
4102 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
4103     GpMatrixOrder order)
4104 {
4105     TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
4106
4107     if(!graphics)
4108         return InvalidParameter;
4109
4110     if(graphics->busy)
4111         return ObjectBusy;
4112
4113     return GdipRotateMatrix(graphics->worldtrans, angle, order);
4114 }
4115
4116 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
4117 {
4118     return GdipBeginContainer2(graphics, state);
4119 }
4120
4121 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics,
4122         GraphicsContainer *state)
4123 {
4124     GraphicsContainerItem *container;
4125     GpStatus sts;
4126
4127     TRACE("(%p, %p)\n", graphics, state);
4128
4129     if(!graphics || !state)
4130         return InvalidParameter;
4131
4132     sts = init_container(&container, graphics);
4133     if(sts != Ok)
4134         return sts;
4135
4136     list_add_head(&graphics->containers, &container->entry);
4137     *state = graphics->contid = container->contid;
4138
4139     return Ok;
4140 }
4141
4142 GpStatus WINGDIPAPI GdipBeginContainer(GpGraphics *graphics, GDIPCONST GpRectF *dstrect, GDIPCONST GpRectF *srcrect, GpUnit unit, GraphicsContainer *state)
4143 {
4144     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4145     return NotImplemented;
4146 }
4147
4148 GpStatus WINGDIPAPI GdipBeginContainerI(GpGraphics *graphics, GDIPCONST GpRect *dstrect, GDIPCONST GpRect *srcrect, GpUnit unit, GraphicsContainer *state)
4149 {
4150     FIXME("(%p, %p, %p, %d, %p): stub\n", graphics, dstrect, srcrect, unit, state);
4151     return NotImplemented;
4152 }
4153
4154 GpStatus WINGDIPAPI GdipComment(GpGraphics *graphics, UINT sizeData, GDIPCONST BYTE *data)
4155 {
4156     FIXME("(%p, %d, %p): stub\n", graphics, sizeData, data);
4157     return NotImplemented;
4158 }
4159
4160 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsContainer state)
4161 {
4162     GpStatus sts;
4163     GraphicsContainerItem *container, *container2;
4164
4165     TRACE("(%p, %x)\n", graphics, state);
4166
4167     if(!graphics)
4168         return InvalidParameter;
4169
4170     LIST_FOR_EACH_ENTRY(container, &graphics->containers, GraphicsContainerItem, entry){
4171         if(container->contid == state)
4172             break;
4173     }
4174
4175     /* did not find a matching container */
4176     if(&container->entry == &graphics->containers)
4177         return Ok;
4178
4179     sts = restore_container(graphics, container);
4180     if(sts != Ok)
4181         return sts;
4182
4183     /* remove all of the containers on top of the found container */
4184     LIST_FOR_EACH_ENTRY_SAFE(container, container2, &graphics->containers, GraphicsContainerItem, entry){
4185         if(container->contid == state)
4186             break;
4187         list_remove(&container->entry);
4188         delete_container(container);
4189     }
4190
4191     list_remove(&container->entry);
4192     delete_container(container);
4193
4194     return Ok;
4195 }
4196
4197 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
4198     REAL sy, GpMatrixOrder order)
4199 {
4200     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
4201
4202     if(!graphics)
4203         return InvalidParameter;
4204
4205     if(graphics->busy)
4206         return ObjectBusy;
4207
4208     return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
4209 }
4210
4211 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
4212     CombineMode mode)
4213 {
4214     TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
4215
4216     if(!graphics || !srcgraphics)
4217         return InvalidParameter;
4218
4219     return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
4220 }
4221
4222 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
4223     CompositingMode mode)
4224 {
4225     TRACE("(%p, %d)\n", graphics, mode);
4226
4227     if(!graphics)
4228         return InvalidParameter;
4229
4230     if(graphics->busy)
4231         return ObjectBusy;
4232
4233     graphics->compmode = mode;
4234
4235     return Ok;
4236 }
4237
4238 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
4239     CompositingQuality quality)
4240 {
4241     TRACE("(%p, %d)\n", graphics, quality);
4242
4243     if(!graphics)
4244         return InvalidParameter;
4245
4246     if(graphics->busy)
4247         return ObjectBusy;
4248
4249     graphics->compqual = quality;
4250
4251     return Ok;
4252 }
4253
4254 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
4255     InterpolationMode mode)
4256 {
4257     TRACE("(%p, %d)\n", graphics, mode);
4258
4259     if(!graphics)
4260         return InvalidParameter;
4261
4262     if(graphics->busy)
4263         return ObjectBusy;
4264
4265     graphics->interpolation = mode;
4266
4267     return Ok;
4268 }
4269
4270 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
4271 {
4272     TRACE("(%p, %.2f)\n", graphics, scale);
4273
4274     if(!graphics || (scale <= 0.0))
4275         return InvalidParameter;
4276
4277     if(graphics->busy)
4278         return ObjectBusy;
4279
4280     graphics->scale = scale;
4281
4282     return Ok;
4283 }
4284
4285 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
4286 {
4287     TRACE("(%p, %d)\n", graphics, unit);
4288
4289     if(!graphics)
4290         return InvalidParameter;
4291
4292     if(graphics->busy)
4293         return ObjectBusy;
4294
4295     if(unit == UnitWorld)
4296         return InvalidParameter;
4297
4298     graphics->unit = unit;
4299
4300     return Ok;
4301 }
4302
4303 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
4304     mode)
4305 {
4306     TRACE("(%p, %d)\n", graphics, mode);
4307
4308     if(!graphics)
4309         return InvalidParameter;
4310
4311     if(graphics->busy)
4312         return ObjectBusy;
4313
4314     graphics->pixeloffset = mode;
4315
4316     return Ok;
4317 }
4318
4319 GpStatus WINGDIPAPI GdipSetRenderingOrigin(GpGraphics *graphics, INT x, INT y)
4320 {
4321     static int calls;
4322
4323     TRACE("(%p,%i,%i)\n", graphics, x, y);
4324
4325     if (!(calls++))
4326         FIXME("not implemented\n");
4327
4328     return NotImplemented;
4329 }
4330
4331 GpStatus WINGDIPAPI GdipGetRenderingOrigin(GpGraphics *graphics, INT *x, INT *y)
4332 {
4333     static int calls;
4334
4335     TRACE("(%p,%p,%p)\n", graphics, x, y);
4336
4337     if (!(calls++))
4338         FIXME("not implemented\n");
4339
4340     *x = *y = 0;
4341
4342     return NotImplemented;
4343 }
4344
4345 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
4346 {
4347     TRACE("(%p, %d)\n", graphics, mode);
4348
4349     if(!graphics)
4350         return InvalidParameter;
4351
4352     if(graphics->busy)
4353         return ObjectBusy;
4354
4355     graphics->smoothing = mode;
4356
4357     return Ok;
4358 }
4359
4360 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
4361 {
4362     TRACE("(%p, %d)\n", graphics, contrast);
4363
4364     if(!graphics)
4365         return InvalidParameter;
4366
4367     graphics->textcontrast = contrast;
4368
4369     return Ok;
4370 }
4371
4372 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
4373     TextRenderingHint hint)
4374 {
4375     TRACE("(%p, %d)\n", graphics, hint);
4376
4377     if(!graphics)
4378         return InvalidParameter;
4379
4380     if(graphics->busy)
4381         return ObjectBusy;
4382
4383     graphics->texthint = hint;
4384
4385     return Ok;
4386 }
4387
4388 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
4389 {
4390     TRACE("(%p, %p)\n", graphics, matrix);
4391
4392     if(!graphics || !matrix)
4393         return InvalidParameter;
4394
4395     if(graphics->busy)
4396         return ObjectBusy;
4397
4398     GdipDeleteMatrix(graphics->worldtrans);
4399     return GdipCloneMatrix(matrix, &graphics->worldtrans);
4400 }
4401
4402 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
4403     REAL dy, GpMatrixOrder order)
4404 {
4405     TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
4406
4407     if(!graphics)
4408         return InvalidParameter;
4409
4410     if(graphics->busy)
4411         return ObjectBusy;
4412
4413     return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
4414 }
4415
4416 /*****************************************************************************
4417  * GdipSetClipHrgn [GDIPLUS.@]
4418  */
4419 GpStatus WINGDIPAPI GdipSetClipHrgn(GpGraphics *graphics, HRGN hrgn, CombineMode mode)
4420 {
4421     GpRegion *region;
4422     GpStatus status;
4423
4424     TRACE("(%p, %p, %d)\n", graphics, hrgn, mode);
4425
4426     if(!graphics)
4427         return InvalidParameter;
4428
4429     status = GdipCreateRegionHrgn(hrgn, &region);
4430     if(status != Ok)
4431         return status;
4432
4433     status = GdipSetClipRegion(graphics, region, mode);
4434
4435     GdipDeleteRegion(region);
4436     return status;
4437 }
4438
4439 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
4440 {
4441     TRACE("(%p, %p, %d)\n", graphics, path, mode);
4442
4443     if(!graphics)
4444         return InvalidParameter;
4445
4446     if(graphics->busy)
4447         return ObjectBusy;
4448
4449     return GdipCombineRegionPath(graphics->clip, path, mode);
4450 }
4451
4452 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
4453                                     REAL width, REAL height,
4454                                     CombineMode mode)
4455 {
4456     GpRectF rect;
4457
4458     TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
4459
4460     if(!graphics)
4461         return InvalidParameter;
4462
4463     if(graphics->busy)
4464         return ObjectBusy;
4465
4466     rect.X = x;
4467     rect.Y = y;
4468     rect.Width  = width;
4469     rect.Height = height;
4470
4471     return GdipCombineRegionRect(graphics->clip, &rect, mode);
4472 }
4473
4474 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
4475                                      INT width, INT height,
4476                                      CombineMode mode)
4477 {
4478     TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
4479
4480     if(!graphics)
4481         return InvalidParameter;
4482
4483     if(graphics->busy)
4484         return ObjectBusy;
4485
4486     return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
4487 }
4488
4489 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
4490                                       CombineMode mode)
4491 {
4492     TRACE("(%p, %p, %d)\n", graphics, region, mode);
4493
4494     if(!graphics || !region)
4495         return InvalidParameter;
4496
4497     if(graphics->busy)
4498         return ObjectBusy;
4499
4500     return GdipCombineRegionRegion(graphics->clip, region, mode);
4501 }
4502
4503 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
4504     UINT limitDpi)
4505 {
4506     static int calls;
4507
4508     TRACE("(%p,%u)\n", metafile, limitDpi);
4509
4510     if(!(calls++))
4511         FIXME("not implemented\n");
4512
4513     return NotImplemented;
4514 }
4515
4516 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
4517     INT count)
4518 {
4519     INT save_state;
4520     POINT *pti;
4521
4522     TRACE("(%p, %p, %d)\n", graphics, points, count);
4523
4524     if(!graphics || !pen || count<=0)
4525         return InvalidParameter;
4526
4527     if(graphics->busy)
4528         return ObjectBusy;
4529
4530     pti = GdipAlloc(sizeof(POINT) * count);
4531
4532     save_state = prepare_dc(graphics, pen);
4533     SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
4534
4535     transform_and_round_points(graphics, pti, (GpPointF*)points, count);
4536     Polygon(graphics->hdc, pti, count);
4537
4538     restore_dc(graphics, save_state);
4539     GdipFree(pti);
4540
4541     return Ok;
4542 }
4543
4544 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
4545     INT count)
4546 {
4547     GpStatus ret;
4548     GpPointF *ptf;
4549     INT i;
4550
4551     TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
4552
4553     if(count<=0)    return InvalidParameter;
4554     ptf = GdipAlloc(sizeof(GpPointF) * count);
4555
4556     for(i = 0;i < count; i++){
4557         ptf[i].X = (REAL)points[i].X;
4558         ptf[i].Y = (REAL)points[i].Y;
4559     }
4560
4561     ret = GdipDrawPolygon(graphics,pen,ptf,count);
4562     GdipFree(ptf);
4563
4564     return ret;
4565 }
4566
4567 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
4568 {
4569     TRACE("(%p, %p)\n", graphics, dpi);
4570
4571     if(!graphics || !dpi)
4572         return InvalidParameter;
4573
4574     if(graphics->busy)
4575         return ObjectBusy;
4576
4577     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
4578
4579     return Ok;
4580 }
4581
4582 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
4583 {
4584     TRACE("(%p, %p)\n", graphics, dpi);
4585
4586     if(!graphics || !dpi)
4587         return InvalidParameter;
4588
4589     if(graphics->busy)
4590         return ObjectBusy;
4591
4592     *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
4593
4594     return Ok;
4595 }
4596
4597 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
4598     GpMatrixOrder order)
4599 {
4600     GpMatrix m;
4601     GpStatus ret;
4602
4603     TRACE("(%p, %p, %d)\n", graphics, matrix, order);
4604
4605     if(!graphics || !matrix)
4606         return InvalidParameter;
4607
4608     if(graphics->busy)
4609         return ObjectBusy;
4610
4611     m = *(graphics->worldtrans);
4612
4613     ret = GdipMultiplyMatrix(&m, matrix, order);
4614     if(ret == Ok)
4615         *(graphics->worldtrans) = m;
4616
4617     return ret;
4618 }
4619
4620 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
4621 {
4622     TRACE("(%p, %p)\n", graphics, hdc);
4623
4624     if(!graphics || !hdc)
4625         return InvalidParameter;
4626
4627     if(graphics->busy)
4628         return ObjectBusy;
4629
4630     if (!graphics->hdc)
4631     {
4632         WARN("no HDC for this graphics\n");
4633         *hdc = NULL;
4634         return GenericError;
4635     }
4636
4637     *hdc = graphics->hdc;
4638     graphics->busy = TRUE;
4639
4640     return Ok;
4641 }
4642
4643 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
4644 {
4645     TRACE("(%p, %p)\n", graphics, hdc);
4646
4647     if(!graphics)
4648         return InvalidParameter;
4649
4650     if(graphics->hdc != hdc || !(graphics->busy))
4651         return InvalidParameter;
4652
4653     graphics->busy = FALSE;
4654
4655     return Ok;
4656 }
4657
4658 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
4659 {
4660     GpRegion *clip;
4661     GpStatus status;
4662
4663     TRACE("(%p, %p)\n", graphics, region);
4664
4665     if(!graphics || !region)
4666         return InvalidParameter;
4667
4668     if(graphics->busy)
4669         return ObjectBusy;
4670
4671     if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
4672         return status;
4673
4674     /* free everything except root node and header */
4675     delete_element(&region->node);
4676     memcpy(region, clip, sizeof(GpRegion));
4677     GdipFree(clip);
4678
4679     return Ok;
4680 }
4681
4682 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
4683                                         GpCoordinateSpace src_space, GpPointF *points, INT count)
4684 {
4685     GpMatrix *matrix;
4686     GpStatus stat;
4687     REAL unitscale;
4688
4689     if(!graphics || !points || count <= 0)
4690         return InvalidParameter;
4691
4692     if(graphics->busy)
4693         return ObjectBusy;
4694
4695     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4696
4697     if (src_space == dst_space) return Ok;
4698
4699     stat = GdipCreateMatrix(&matrix);
4700     if (stat == Ok)
4701     {
4702         unitscale = convert_unit(graphics_res(graphics), graphics->unit);
4703
4704         if(graphics->unit != UnitDisplay)
4705             unitscale *= graphics->scale;
4706
4707         /* transform from src_space to CoordinateSpacePage */
4708         switch (src_space)
4709         {
4710         case CoordinateSpaceWorld:
4711             GdipMultiplyMatrix(matrix, graphics->worldtrans, MatrixOrderAppend);
4712             break;
4713         case CoordinateSpacePage:
4714             break;
4715         case CoordinateSpaceDevice:
4716             GdipScaleMatrix(matrix, 1.0/unitscale, 1.0/unitscale, MatrixOrderAppend);
4717             break;
4718         }
4719
4720         /* transform from CoordinateSpacePage to dst_space */
4721         switch (dst_space)
4722         {
4723         case CoordinateSpaceWorld:
4724             {
4725                 GpMatrix *inverted_transform;
4726                 stat = GdipCloneMatrix(graphics->worldtrans, &inverted_transform);
4727                 if (stat == Ok)
4728                 {
4729                     stat = GdipInvertMatrix(inverted_transform);
4730                     if (stat == Ok)
4731                         GdipMultiplyMatrix(matrix, inverted_transform, MatrixOrderAppend);
4732                     GdipDeleteMatrix(inverted_transform);
4733                 }
4734                 break;
4735             }
4736         case CoordinateSpacePage:
4737             break;
4738         case CoordinateSpaceDevice:
4739             GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
4740             break;
4741         }
4742
4743         if (stat == Ok)
4744             stat = GdipTransformMatrixPoints(matrix, points, count);
4745
4746         GdipDeleteMatrix(matrix);
4747     }
4748
4749     return stat;
4750 }
4751
4752 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
4753                                          GpCoordinateSpace src_space, GpPoint *points, INT count)
4754 {
4755     GpPointF *pointsF;
4756     GpStatus ret;
4757     INT i;
4758
4759     TRACE("(%p, %d, %d, %p, %d)\n", graphics, dst_space, src_space, points, count);
4760
4761     if(count <= 0)
4762         return InvalidParameter;
4763
4764     pointsF = GdipAlloc(sizeof(GpPointF) * count);
4765     if(!pointsF)
4766         return OutOfMemory;
4767
4768     for(i = 0; i < count; i++){
4769         pointsF[i].X = (REAL)points[i].X;
4770         pointsF[i].Y = (REAL)points[i].Y;
4771     }
4772
4773     ret = GdipTransformPoints(graphics, dst_space, src_space, pointsF, count);
4774
4775     if(ret == Ok)
4776         for(i = 0; i < count; i++){
4777             points[i].X = roundr(pointsF[i].X);
4778             points[i].Y = roundr(pointsF[i].Y);
4779         }
4780     GdipFree(pointsF);
4781
4782     return ret;
4783 }
4784
4785 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
4786 {
4787     static int calls;
4788
4789     TRACE("\n");
4790
4791     if (!calls++)
4792       FIXME("stub\n");
4793
4794     return NULL;
4795 }
4796
4797 /*****************************************************************************
4798  * GdipTranslateClip [GDIPLUS.@]
4799  */
4800 GpStatus WINGDIPAPI GdipTranslateClip(GpGraphics *graphics, REAL dx, REAL dy)
4801 {
4802     TRACE("(%p, %.2f, %.2f)\n", graphics, dx, dy);
4803
4804     if(!graphics)
4805         return InvalidParameter;
4806
4807     if(graphics->busy)
4808         return ObjectBusy;
4809
4810     return GdipTranslateRegion(graphics->clip, dx, dy);
4811 }
4812
4813 /*****************************************************************************
4814  * GdipTranslateClipI [GDIPLUS.@]
4815  */
4816 GpStatus WINGDIPAPI GdipTranslateClipI(GpGraphics *graphics, INT dx, INT dy)
4817 {
4818     TRACE("(%p, %d, %d)\n", graphics, dx, dy);
4819
4820     if(!graphics)
4821         return InvalidParameter;
4822
4823     if(graphics->busy)
4824         return ObjectBusy;
4825
4826     return GdipTranslateRegion(graphics->clip, (REAL)dx, (REAL)dy);
4827 }
4828
4829
4830 /*****************************************************************************
4831  * GdipMeasureDriverString [GDIPLUS.@]
4832  */
4833 GpStatus WINGDIPAPI GdipMeasureDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4834                                             GDIPCONST GpFont *font, GDIPCONST PointF *positions,
4835                                             INT flags, GDIPCONST GpMatrix *matrix, RectF *boundingBox)
4836 {
4837     FIXME("(%p %p %d %p %p %d %p %p): stub\n", graphics, text, length, font, positions, flags, matrix, boundingBox);
4838     return NotImplemented;
4839 }
4840
4841 /*****************************************************************************
4842  * GdipDrawDriverString [GDIPLUS.@]
4843  */
4844 GpStatus WINGDIPAPI GdipDrawDriverString(GpGraphics *graphics, GDIPCONST UINT16 *text, INT length,
4845                                          GDIPCONST GpFont *font, GDIPCONST GpBrush *brush,
4846                                          GDIPCONST PointF *positions, INT flags,
4847                                          GDIPCONST GpMatrix *matrix )
4848 {
4849     FIXME("(%p %p %d %p %p %p %d %p): stub\n", graphics, text, length, font, brush, positions, flags, matrix);
4850     return NotImplemented;
4851 }
4852
4853 GpStatus WINGDIPAPI GdipRecordMetafile(HDC hdc, EmfType type, GDIPCONST GpRectF *frameRect,
4854                                        MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4855 {
4856     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4857     return NotImplemented;
4858 }
4859
4860 /*****************************************************************************
4861  * GdipRecordMetafileI [GDIPLUS.@]
4862  */
4863 GpStatus WINGDIPAPI GdipRecordMetafileI(HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4864                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4865 {
4866     FIXME("(%p %d %p %d %p %p): stub\n", hdc, type, frameRect, frameUnit, desc, metafile);
4867     return NotImplemented;
4868 }
4869
4870 GpStatus WINGDIPAPI GdipRecordMetafileStream(IStream *stream, HDC hdc, EmfType type, GDIPCONST GpRect *frameRect,
4871                                         MetafileFrameUnit frameUnit, GDIPCONST WCHAR *desc, GpMetafile **metafile)
4872 {
4873     FIXME("(%p %p %d %p %d %p %p): stub\n", stream, hdc, type, frameRect, frameUnit, desc, metafile);
4874     return NotImplemented;
4875 }
4876
4877 /*****************************************************************************
4878  * GdipIsVisibleClipEmpty [GDIPLUS.@]
4879  */
4880 GpStatus WINGDIPAPI GdipIsVisibleClipEmpty(GpGraphics *graphics, BOOL *res)
4881 {
4882     GpStatus stat;
4883     GpRegion* rgn;
4884
4885     TRACE("(%p, %p)\n", graphics, res);
4886
4887     if((stat = GdipCreateRegion(&rgn)) != Ok)
4888         return stat;
4889
4890     if((stat = get_visible_clip_region(graphics, rgn)) != Ok)
4891         goto cleanup;
4892
4893     stat = GdipIsEmptyRegion(rgn, graphics, res);
4894
4895 cleanup:
4896     GdipDeleteRegion(rgn);
4897     return stat;
4898 }