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