gdiplus: Implement GdipRegionGetHRgn for paths.
[wine/multimedia.git] / dlls / gdiplus / graphics.c
blobb9cb1c2f97ad345bac8559bd5261e66867b34da1
1 /*
2 * Copyright (C) 2007 Google (Evan Stade)
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.
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.
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
19 #include <stdarg.h>
20 #include <math.h>
21 #include <limits.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "wingdi.h"
27 #include "wine/unicode.h"
29 #define COBJMACROS
30 #include "objbase.h"
31 #include "ocidl.h"
32 #include "olectl.h"
33 #include "ole2.h"
35 #include "winreg.h"
36 #include "shlwapi.h"
38 #include "gdiplus.h"
39 #include "gdiplus_private.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
44 /* looks-right constants */
45 #define ANCHOR_WIDTH (2.0)
46 #define MAX_ITERS (50)
48 /* Converts angle (in degrees) to x/y coordinates */
49 static void deg2xy(REAL angle, REAL x_0, REAL y_0, REAL *x, REAL *y)
51 REAL radAngle, hypotenuse;
53 radAngle = deg2rad(angle);
54 hypotenuse = 50.0; /* arbitrary */
56 *x = x_0 + cos(radAngle) * hypotenuse;
57 *y = y_0 + sin(radAngle) * hypotenuse;
60 /* Converts from gdiplus path point type to gdi path point type. */
61 static BYTE convert_path_point_type(BYTE type)
63 BYTE ret;
65 switch(type & PathPointTypePathTypeMask){
66 case PathPointTypeBezier:
67 ret = PT_BEZIERTO;
68 break;
69 case PathPointTypeLine:
70 ret = PT_LINETO;
71 break;
72 case PathPointTypeStart:
73 ret = PT_MOVETO;
74 break;
75 default:
76 ERR("Bad point type\n");
77 return 0;
80 if(type & PathPointTypeCloseSubpath)
81 ret |= PT_CLOSEFIGURE;
83 return ret;
86 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
88 HPEN gdipen;
89 REAL width;
90 INT save_state = SaveDC(graphics->hdc), i, numdashes;
91 GpPointF pt[2];
92 DWORD dash_array[MAX_DASHLEN];
94 EndPath(graphics->hdc);
96 if(pen->unit == UnitPixel){
97 width = pen->width;
99 else{
100 /* Get an estimate for the amount the pen width is affected by the world
101 * transform. (This is similar to what some of the wine drivers do.) */
102 pt[0].X = 0.0;
103 pt[0].Y = 0.0;
104 pt[1].X = 1.0;
105 pt[1].Y = 1.0;
106 GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
107 width = sqrt((pt[1].X - pt[0].X) * (pt[1].X - pt[0].X) +
108 (pt[1].Y - pt[0].Y) * (pt[1].Y - pt[0].Y)) / sqrt(2.0);
110 width *= pen->width * convert_unit(graphics->hdc,
111 pen->unit == UnitWorld ? graphics->unit : pen->unit);
114 if(pen->dash == DashStyleCustom){
115 numdashes = min(pen->numdashes, MAX_DASHLEN);
117 TRACE("dashes are: ");
118 for(i = 0; i < numdashes; i++){
119 dash_array[i] = roundr(width * pen->dashes[i]);
120 TRACE("%d, ", dash_array[i]);
122 TRACE("\n and the pen style is %x\n", pen->style);
124 gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
125 numdashes, dash_array);
127 else
128 gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
130 SelectObject(graphics->hdc, gdipen);
132 return save_state;
135 static void restore_dc(GpGraphics *graphics, INT state)
137 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
138 RestoreDC(graphics->hdc, state);
141 /* This helper applies all the changes that the points listed in ptf need in
142 * order to be drawn on the device context. In the end, this should include at
143 * least:
144 * -scaling by page unit
145 * -applying world transformation
146 * -converting from float to int
147 * Native gdiplus uses gdi32 to do all this (via SetMapMode, SetViewportExtEx,
148 * SetWindowExtEx, SetWorldTransform, etc.) but we cannot because we are using
149 * gdi to draw, and these functions would irreparably mess with line widths.
151 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
152 GpPointF *ptf, INT count)
154 REAL unitscale;
155 GpMatrix *matrix;
156 int i;
158 unitscale = convert_unit(graphics->hdc, graphics->unit);
160 /* apply page scale */
161 if(graphics->unit != UnitDisplay)
162 unitscale *= graphics->scale;
164 GdipCloneMatrix(graphics->worldtrans, &matrix);
165 GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
166 GdipTransformMatrixPoints(matrix, ptf, count);
167 GdipDeleteMatrix(matrix);
169 for(i = 0; i < count; i++){
170 pti[i].x = roundr(ptf[i].X);
171 pti[i].y = roundr(ptf[i].Y);
175 /* GdipDrawPie/GdipFillPie helper function */
176 static void draw_pie(GpGraphics *graphics, REAL x, REAL y, REAL width,
177 REAL height, REAL startAngle, REAL sweepAngle)
179 GpPointF ptf[4];
180 POINT pti[4];
182 ptf[0].X = x;
183 ptf[0].Y = y;
184 ptf[1].X = x + width;
185 ptf[1].Y = y + height;
187 deg2xy(startAngle+sweepAngle, x + width / 2.0, y + width / 2.0, &ptf[2].X, &ptf[2].Y);
188 deg2xy(startAngle, x + width / 2.0, y + width / 2.0, &ptf[3].X, &ptf[3].Y);
190 transform_and_round_points(graphics, pti, ptf, 4);
192 Pie(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y, pti[2].x,
193 pti[2].y, pti[3].x, pti[3].y);
196 /* Draws the linecap the specified color and size on the hdc. The linecap is in
197 * direction of the line from x1, y1 to x2, y2 and is anchored on x2, y2. Probably
198 * should not be called on an hdc that has a path you care about. */
199 static void draw_cap(GpGraphics *graphics, COLORREF color, GpLineCap cap, REAL size,
200 const GpCustomLineCap *custom, REAL x1, REAL y1, REAL x2, REAL y2)
202 HGDIOBJ oldbrush = NULL, oldpen = NULL;
203 GpMatrix *matrix = NULL;
204 HBRUSH brush = NULL;
205 HPEN pen = NULL;
206 PointF ptf[4], *custptf = NULL;
207 POINT pt[4], *custpt = NULL;
208 BYTE *tp = NULL;
209 REAL theta, dsmall, dbig, dx, dy = 0.0;
210 INT i, count;
211 LOGBRUSH lb;
212 BOOL customstroke;
214 if((x1 == x2) && (y1 == y2))
215 return;
217 theta = gdiplus_atan2(y2 - y1, x2 - x1);
219 customstroke = (cap == LineCapCustom) && custom && (!custom->fill);
220 if(!customstroke){
221 brush = CreateSolidBrush(color);
222 lb.lbStyle = BS_SOLID;
223 lb.lbColor = color;
224 lb.lbHatch = 0;
225 pen = ExtCreatePen(PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT |
226 PS_JOIN_MITER, 1, &lb, 0,
227 NULL);
228 oldbrush = SelectObject(graphics->hdc, brush);
229 oldpen = SelectObject(graphics->hdc, pen);
232 switch(cap){
233 case LineCapFlat:
234 break;
235 case LineCapSquare:
236 case LineCapSquareAnchor:
237 case LineCapDiamondAnchor:
238 size = size * (cap & LineCapNoAnchor ? ANCHOR_WIDTH : 1.0) / 2.0;
239 if(cap == LineCapDiamondAnchor){
240 dsmall = cos(theta + M_PI_2) * size;
241 dbig = sin(theta + M_PI_2) * size;
243 else{
244 dsmall = cos(theta + M_PI_4) * size;
245 dbig = sin(theta + M_PI_4) * size;
248 ptf[0].X = x2 - dsmall;
249 ptf[1].X = x2 + dbig;
251 ptf[0].Y = y2 - dbig;
252 ptf[3].Y = y2 + dsmall;
254 ptf[1].Y = y2 - dsmall;
255 ptf[2].Y = y2 + dbig;
257 ptf[3].X = x2 - dbig;
258 ptf[2].X = x2 + dsmall;
260 transform_and_round_points(graphics, pt, ptf, 4);
261 Polygon(graphics->hdc, pt, 4);
263 break;
264 case LineCapArrowAnchor:
265 size = size * 4.0 / sqrt(3.0);
267 dx = cos(M_PI / 6.0 + theta) * size;
268 dy = sin(M_PI / 6.0 + theta) * size;
270 ptf[0].X = x2 - dx;
271 ptf[0].Y = y2 - dy;
273 dx = cos(- M_PI / 6.0 + theta) * size;
274 dy = sin(- M_PI / 6.0 + theta) * size;
276 ptf[1].X = x2 - dx;
277 ptf[1].Y = y2 - dy;
279 ptf[2].X = x2;
280 ptf[2].Y = y2;
282 transform_and_round_points(graphics, pt, ptf, 3);
283 Polygon(graphics->hdc, pt, 3);
285 break;
286 case LineCapRoundAnchor:
287 dx = dy = ANCHOR_WIDTH * size / 2.0;
289 ptf[0].X = x2 - dx;
290 ptf[0].Y = y2 - dy;
291 ptf[1].X = x2 + dx;
292 ptf[1].Y = y2 + dy;
294 transform_and_round_points(graphics, pt, ptf, 2);
295 Ellipse(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y);
297 break;
298 case LineCapTriangle:
299 size = size / 2.0;
300 dx = cos(M_PI_2 + theta) * size;
301 dy = sin(M_PI_2 + theta) * size;
303 ptf[0].X = x2 - dx;
304 ptf[0].Y = y2 - dy;
305 ptf[1].X = x2 + dx;
306 ptf[1].Y = y2 + dy;
308 dx = cos(theta) * size;
309 dy = sin(theta) * size;
311 ptf[2].X = x2 + dx;
312 ptf[2].Y = y2 + dy;
314 transform_and_round_points(graphics, pt, ptf, 3);
315 Polygon(graphics->hdc, pt, 3);
317 break;
318 case LineCapRound:
319 dx = dy = size / 2.0;
321 ptf[0].X = x2 - dx;
322 ptf[0].Y = y2 - dy;
323 ptf[1].X = x2 + dx;
324 ptf[1].Y = y2 + dy;
326 dx = -cos(M_PI_2 + theta) * size;
327 dy = -sin(M_PI_2 + theta) * size;
329 ptf[2].X = x2 - dx;
330 ptf[2].Y = y2 - dy;
331 ptf[3].X = x2 + dx;
332 ptf[3].Y = y2 + dy;
334 transform_and_round_points(graphics, pt, ptf, 4);
335 Pie(graphics->hdc, pt[0].x, pt[0].y, pt[1].x, pt[1].y, pt[2].x,
336 pt[2].y, pt[3].x, pt[3].y);
338 break;
339 case LineCapCustom:
340 if(!custom)
341 break;
343 count = custom->pathdata.Count;
344 custptf = GdipAlloc(count * sizeof(PointF));
345 custpt = GdipAlloc(count * sizeof(POINT));
346 tp = GdipAlloc(count);
348 if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
349 goto custend;
351 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
353 GdipScaleMatrix(matrix, size, size, MatrixOrderAppend);
354 GdipRotateMatrix(matrix, (180.0 / M_PI) * (theta - M_PI_2),
355 MatrixOrderAppend);
356 GdipTranslateMatrix(matrix, x2, y2, MatrixOrderAppend);
357 GdipTransformMatrixPoints(matrix, custptf, count);
359 transform_and_round_points(graphics, custpt, custptf, count);
361 for(i = 0; i < count; i++)
362 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
364 if(custom->fill){
365 BeginPath(graphics->hdc);
366 PolyDraw(graphics->hdc, custpt, tp, count);
367 EndPath(graphics->hdc);
368 StrokeAndFillPath(graphics->hdc);
370 else
371 PolyDraw(graphics->hdc, custpt, tp, count);
373 custend:
374 GdipFree(custptf);
375 GdipFree(custpt);
376 GdipFree(tp);
377 GdipDeleteMatrix(matrix);
378 break;
379 default:
380 break;
383 if(!customstroke){
384 SelectObject(graphics->hdc, oldbrush);
385 SelectObject(graphics->hdc, oldpen);
386 DeleteObject(brush);
387 DeleteObject(pen);
391 /* Shortens the line by the given percent by changing x2, y2.
392 * If percent is > 1.0 then the line will change direction.
393 * If percent is negative it can lengthen the line. */
394 static void shorten_line_percent(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL percent)
396 REAL dist, theta, dx, dy;
398 if((y1 == *y2) && (x1 == *x2))
399 return;
401 dist = sqrt((*x2 - x1) * (*x2 - x1) + (*y2 - y1) * (*y2 - y1)) * -percent;
402 theta = gdiplus_atan2((*y2 - y1), (*x2 - x1));
403 dx = cos(theta) * dist;
404 dy = sin(theta) * dist;
406 *x2 = *x2 + dx;
407 *y2 = *y2 + dy;
410 /* Shortens the line by the given amount by changing x2, y2.
411 * If the amount is greater than the distance, the line will become length 0.
412 * If the amount is negative, it can lengthen the line. */
413 static void shorten_line_amt(REAL x1, REAL y1, REAL *x2, REAL *y2, REAL amt)
415 REAL dx, dy, percent;
417 dx = *x2 - x1;
418 dy = *y2 - y1;
419 if(dx == 0 && dy == 0)
420 return;
422 percent = amt / sqrt(dx * dx + dy * dy);
423 if(percent >= 1.0){
424 *x2 = x1;
425 *y2 = y1;
426 return;
429 shorten_line_percent(x1, y1, x2, y2, percent);
432 /* Draws lines between the given points, and if caps is true then draws an endcap
433 * at the end of the last line. */
434 static GpStatus draw_polyline(GpGraphics *graphics, GpPen *pen,
435 GDIPCONST GpPointF * pt, INT count, BOOL caps)
437 POINT *pti = NULL;
438 GpPointF *ptcopy = NULL;
439 GpStatus status = GenericError;
441 if(!count)
442 return Ok;
444 pti = GdipAlloc(count * sizeof(POINT));
445 ptcopy = GdipAlloc(count * sizeof(GpPointF));
447 if(!pti || !ptcopy){
448 status = OutOfMemory;
449 goto end;
452 memcpy(ptcopy, pt, count * sizeof(GpPointF));
454 if(caps){
455 if(pen->endcap == LineCapArrowAnchor)
456 shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
457 &ptcopy[count-1].X, &ptcopy[count-1].Y, pen->width);
458 else if((pen->endcap == LineCapCustom) && pen->customend)
459 shorten_line_amt(ptcopy[count-2].X, ptcopy[count-2].Y,
460 &ptcopy[count-1].X, &ptcopy[count-1].Y,
461 pen->customend->inset * pen->width);
463 if(pen->startcap == LineCapArrowAnchor)
464 shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
465 &ptcopy[0].X, &ptcopy[0].Y, pen->width);
466 else if((pen->startcap == LineCapCustom) && pen->customstart)
467 shorten_line_amt(ptcopy[1].X, ptcopy[1].Y,
468 &ptcopy[0].X, &ptcopy[0].Y,
469 pen->customstart->inset * pen->width);
471 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
472 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X, pt[count - 1].Y);
473 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
474 pt[1].X, pt[1].Y, pt[0].X, pt[0].Y);
477 transform_and_round_points(graphics, pti, ptcopy, count);
479 if(Polyline(graphics->hdc, pti, count))
480 status = Ok;
482 end:
483 GdipFree(pti);
484 GdipFree(ptcopy);
486 return status;
489 /* Conducts a linear search to find the bezier points that will back off
490 * the endpoint of the curve by a distance of amt. Linear search works
491 * better than binary in this case because there are multiple solutions,
492 * and binary searches often find a bad one. I don't think this is what
493 * Windows does but short of rendering the bezier without GDI's help it's
494 * the best we can do. If rev then work from the start of the passed points
495 * instead of the end. */
496 static void shorten_bezier_amt(GpPointF * pt, REAL amt, BOOL rev)
498 GpPointF origpt[4];
499 REAL percent = 0.00, dx, dy, origx, origy, diff = -1.0;
500 INT i, first = 0, second = 1, third = 2, fourth = 3;
502 if(rev){
503 first = 3;
504 second = 2;
505 third = 1;
506 fourth = 0;
509 origx = pt[fourth].X;
510 origy = pt[fourth].Y;
511 memcpy(origpt, pt, sizeof(GpPointF) * 4);
513 for(i = 0; (i < MAX_ITERS) && (diff < amt); i++){
514 /* reset bezier points to original values */
515 memcpy(pt, origpt, sizeof(GpPointF) * 4);
516 /* Perform magic on bezier points. Order is important here.*/
517 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
518 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
519 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
520 shorten_line_percent(pt[first].X, pt[first].Y, &pt[second].X, &pt[second].Y, percent);
521 shorten_line_percent(pt[second].X, pt[second].Y, &pt[third].X, &pt[third].Y, percent);
522 shorten_line_percent(pt[third].X, pt[third].Y, &pt[fourth].X, &pt[fourth].Y, percent);
524 dx = pt[fourth].X - origx;
525 dy = pt[fourth].Y - origy;
527 diff = sqrt(dx * dx + dy * dy);
528 percent += 0.0005 * amt;
532 /* Draws bezier curves between given points, and if caps is true then draws an
533 * endcap at the end of the last line. */
534 static GpStatus draw_polybezier(GpGraphics *graphics, GpPen *pen,
535 GDIPCONST GpPointF * pt, INT count, BOOL caps)
537 POINT *pti;
538 GpPointF *ptcopy;
539 GpStatus status = GenericError;
541 if(!count)
542 return Ok;
544 pti = GdipAlloc(count * sizeof(POINT));
545 ptcopy = GdipAlloc(count * sizeof(GpPointF));
547 if(!pti || !ptcopy){
548 status = OutOfMemory;
549 goto end;
552 memcpy(ptcopy, pt, count * sizeof(GpPointF));
554 if(caps){
555 if(pen->endcap == LineCapArrowAnchor)
556 shorten_bezier_amt(&ptcopy[count-4], pen->width, FALSE);
557 else if((pen->endcap == LineCapCustom) && pen->customend)
558 shorten_bezier_amt(&ptcopy[count-4], pen->width * pen->customend->inset,
559 FALSE);
561 if(pen->startcap == LineCapArrowAnchor)
562 shorten_bezier_amt(ptcopy, pen->width, TRUE);
563 else if((pen->startcap == LineCapCustom) && pen->customstart)
564 shorten_bezier_amt(ptcopy, pen->width * pen->customstart->inset, TRUE);
566 /* the direction of the line cap is parallel to the direction at the
567 * end of the bezier (which, if it has been shortened, is not the same
568 * as the direction from pt[count-2] to pt[count-1]) */
569 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
570 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
571 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
572 pt[count - 1].X, pt[count - 1].Y);
574 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
575 pt[0].X - (ptcopy[0].X - ptcopy[1].X),
576 pt[0].Y - (ptcopy[0].Y - ptcopy[1].Y), pt[0].X, pt[0].Y);
579 transform_and_round_points(graphics, pti, ptcopy, count);
581 PolyBezier(graphics->hdc, pti, count);
583 status = Ok;
585 end:
586 GdipFree(pti);
587 GdipFree(ptcopy);
589 return status;
592 /* Draws a combination of bezier curves and lines between points. */
593 static GpStatus draw_poly(GpGraphics *graphics, GpPen *pen, GDIPCONST GpPointF * pt,
594 GDIPCONST BYTE * types, INT count, BOOL caps)
596 POINT *pti = GdipAlloc(count * sizeof(POINT));
597 BYTE *tp = GdipAlloc(count);
598 GpPointF *ptcopy = GdipAlloc(count * sizeof(GpPointF));
599 INT i, j;
600 GpStatus status = GenericError;
602 if(!count){
603 status = Ok;
604 goto end;
606 if(!pti || !tp || !ptcopy){
607 status = OutOfMemory;
608 goto end;
611 for(i = 1; i < count; i++){
612 if((types[i] & PathPointTypePathTypeMask) == PathPointTypeBezier){
613 if((i + 2 >= count) || !(types[i + 1] & PathPointTypeBezier)
614 || !(types[i + 1] & PathPointTypeBezier)){
615 ERR("Bad bezier points\n");
616 goto end;
618 i += 2;
622 memcpy(ptcopy, pt, count * sizeof(GpPointF));
624 /* If we are drawing caps, go through the points and adjust them accordingly,
625 * and draw the caps. */
626 if(caps){
627 switch(types[count - 1] & PathPointTypePathTypeMask){
628 case PathPointTypeBezier:
629 if(pen->endcap == LineCapArrowAnchor)
630 shorten_bezier_amt(&ptcopy[count - 4], pen->width, FALSE);
631 else if((pen->endcap == LineCapCustom) && pen->customend)
632 shorten_bezier_amt(&ptcopy[count - 4],
633 pen->width * pen->customend->inset, FALSE);
635 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
636 pt[count - 1].X - (ptcopy[count - 1].X - ptcopy[count - 2].X),
637 pt[count - 1].Y - (ptcopy[count - 1].Y - ptcopy[count - 2].Y),
638 pt[count - 1].X, pt[count - 1].Y);
640 break;
641 case PathPointTypeLine:
642 if(pen->endcap == LineCapArrowAnchor)
643 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
644 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
645 pen->width);
646 else if((pen->endcap == LineCapCustom) && pen->customend)
647 shorten_line_amt(ptcopy[count - 2].X, ptcopy[count - 2].Y,
648 &ptcopy[count - 1].X, &ptcopy[count - 1].Y,
649 pen->customend->inset * pen->width);
651 draw_cap(graphics, pen->brush->lb.lbColor, pen->endcap, pen->width, pen->customend,
652 pt[count - 2].X, pt[count - 2].Y, pt[count - 1].X,
653 pt[count - 1].Y);
655 break;
656 default:
657 ERR("Bad path last point\n");
658 goto end;
661 /* Find start of points */
662 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
663 == PathPointTypeStart); j++);
665 switch(types[j] & PathPointTypePathTypeMask){
666 case PathPointTypeBezier:
667 if(pen->startcap == LineCapArrowAnchor)
668 shorten_bezier_amt(&ptcopy[j - 1], pen->width, TRUE);
669 else if((pen->startcap == LineCapCustom) && pen->customstart)
670 shorten_bezier_amt(&ptcopy[j - 1],
671 pen->width * pen->customstart->inset, TRUE);
673 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
674 pt[j - 1].X - (ptcopy[j - 1].X - ptcopy[j].X),
675 pt[j - 1].Y - (ptcopy[j - 1].Y - ptcopy[j].Y),
676 pt[j - 1].X, pt[j - 1].Y);
678 break;
679 case PathPointTypeLine:
680 if(pen->startcap == LineCapArrowAnchor)
681 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
682 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
683 pen->width);
684 else if((pen->startcap == LineCapCustom) && pen->customstart)
685 shorten_line_amt(ptcopy[j].X, ptcopy[j].Y,
686 &ptcopy[j - 1].X, &ptcopy[j - 1].Y,
687 pen->customstart->inset * pen->width);
689 draw_cap(graphics, pen->brush->lb.lbColor, pen->startcap, pen->width, pen->customstart,
690 pt[j].X, pt[j].Y, pt[j - 1].X,
691 pt[j - 1].Y);
693 break;
694 default:
695 ERR("Bad path points\n");
696 goto end;
700 transform_and_round_points(graphics, pti, ptcopy, count);
702 for(i = 0; i < count; i++){
703 tp[i] = convert_path_point_type(types[i]);
706 PolyDraw(graphics->hdc, pti, tp, count);
708 status = Ok;
710 end:
711 GdipFree(pti);
712 GdipFree(ptcopy);
713 GdipFree(tp);
715 return status;
718 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
720 GpStatus result;
722 BeginPath(graphics->hdc);
723 result = draw_poly(graphics, NULL, path->pathdata.Points,
724 path->pathdata.Types, path->pathdata.Count, FALSE);
725 EndPath(graphics->hdc);
726 return result;
729 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
731 TRACE("(%p, %p)\n", hdc, graphics);
733 return GdipCreateFromHDC2(hdc, NULL, graphics);
736 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
738 GpStatus retval;
740 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
742 if(hDevice != NULL) {
743 FIXME("Don't know how to hadle parameter hDevice\n");
744 return NotImplemented;
747 if(hdc == NULL)
748 return OutOfMemory;
750 if(graphics == NULL)
751 return InvalidParameter;
753 *graphics = GdipAlloc(sizeof(GpGraphics));
754 if(!*graphics) return OutOfMemory;
756 if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
757 GdipFree(*graphics);
758 return retval;
761 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
762 GdipFree((*graphics)->worldtrans);
763 GdipFree(*graphics);
764 return retval;
767 (*graphics)->hdc = hdc;
768 (*graphics)->hwnd = WindowFromDC(hdc);
769 (*graphics)->smoothing = SmoothingModeDefault;
770 (*graphics)->compqual = CompositingQualityDefault;
771 (*graphics)->interpolation = InterpolationModeDefault;
772 (*graphics)->pixeloffset = PixelOffsetModeDefault;
773 (*graphics)->compmode = CompositingModeSourceOver;
774 (*graphics)->unit = UnitDisplay;
775 (*graphics)->scale = 1.0;
776 (*graphics)->busy = FALSE;
777 (*graphics)->textcontrast = 4;
779 return Ok;
782 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
784 GpStatus ret;
786 TRACE("(%p, %p)\n", hwnd, graphics);
788 if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok)
789 return ret;
791 (*graphics)->hwnd = hwnd;
793 return Ok;
796 /* FIXME: no icm handling */
797 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
799 TRACE("(%p, %p)\n", hwnd, graphics);
801 return GdipCreateFromHWND(hwnd, graphics);
804 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
805 GpMetafile **metafile)
807 static int calls;
809 if(!hemf || !metafile)
810 return InvalidParameter;
812 if(!(calls++))
813 FIXME("not implemented\n");
815 return NotImplemented;
818 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
819 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
821 IStream *stream = NULL;
822 UINT read;
823 BYTE* copy;
824 HENHMETAFILE hemf;
825 GpStatus retval = GenericError;
827 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
829 if(!hwmf || !metafile || !placeable)
830 return InvalidParameter;
832 *metafile = NULL;
833 read = GetMetaFileBitsEx(hwmf, 0, NULL);
834 if(!read)
835 return GenericError;
836 copy = GdipAlloc(read);
837 GetMetaFileBitsEx(hwmf, read, copy);
839 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
840 GdipFree(copy);
842 read = GetEnhMetaFileBits(hemf, 0, NULL);
843 copy = GdipAlloc(read);
844 GetEnhMetaFileBits(hemf, read, copy);
845 DeleteEnhMetaFile(hemf);
847 if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
848 ERR("could not make stream\n");
849 GdipFree(copy);
850 goto err;
853 *metafile = GdipAlloc(sizeof(GpMetafile));
854 if(!*metafile){
855 retval = OutOfMemory;
856 goto err;
859 if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
860 (LPVOID*) &((*metafile)->image.picture)) != S_OK)
861 goto err;
864 (*metafile)->image.type = ImageTypeMetafile;
865 (*metafile)->bounds.X = ((REAL) placeable->BoundingBox.Left) / ((REAL) placeable->Inch);
866 (*metafile)->bounds.Y = ((REAL) placeable->BoundingBox.Right) / ((REAL) placeable->Inch);
867 (*metafile)->bounds.Width = ((REAL) (placeable->BoundingBox.Right
868 - placeable->BoundingBox.Left)) / ((REAL) placeable->Inch);
869 (*metafile)->bounds.Height = ((REAL) (placeable->BoundingBox.Bottom
870 - placeable->BoundingBox.Top)) / ((REAL) placeable->Inch);
871 (*metafile)->unit = UnitInch;
873 if(delete)
874 DeleteMetaFile(hwmf);
876 return Ok;
878 err:
879 GdipFree(*metafile);
880 IStream_Release(stream);
881 return retval;
884 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
885 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
887 HMETAFILE hmf = GetMetaFileW(file);
889 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
891 if(!hmf) return InvalidParameter;
893 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
896 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
897 UINT access, IStream **stream)
899 DWORD dwMode;
900 HRESULT ret;
902 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
904 if(!stream || !filename)
905 return InvalidParameter;
907 if(access & GENERIC_WRITE)
908 dwMode = STGM_SHARE_DENY_WRITE | STGM_WRITE | STGM_CREATE;
909 else if(access & GENERIC_READ)
910 dwMode = STGM_SHARE_DENY_WRITE | STGM_READ | STGM_FAILIFTHERE;
911 else
912 return InvalidParameter;
914 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
916 return hresult_to_status(ret);
919 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
921 TRACE("(%p)\n", graphics);
923 if(!graphics) return InvalidParameter;
924 if(graphics->busy) return ObjectBusy;
926 if(graphics->hwnd)
927 ReleaseDC(graphics->hwnd, graphics->hdc);
929 GdipDeleteRegion(graphics->clip);
930 GdipDeleteMatrix(graphics->worldtrans);
931 GdipFree(graphics);
933 return Ok;
936 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
937 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
939 INT save_state, num_pts;
940 GpPointF points[MAX_ARC_PTS];
941 GpStatus retval;
943 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
944 width, height, startAngle, sweepAngle);
946 if(!graphics || !pen || width <= 0 || height <= 0)
947 return InvalidParameter;
949 if(graphics->busy)
950 return ObjectBusy;
952 num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
954 save_state = prepare_dc(graphics, pen);
956 retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
958 restore_dc(graphics, save_state);
960 return retval;
963 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
964 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
966 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
967 width, height, startAngle, sweepAngle);
969 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
972 GpStatus WINGDIPAPI GdipDrawBezier(GpGraphics *graphics, GpPen *pen, REAL x1,
973 REAL y1, REAL x2, REAL y2, REAL x3, REAL y3, REAL x4, REAL y4)
975 INT save_state;
976 GpPointF pt[4];
977 GpStatus retval;
979 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
980 x2, y2, x3, y3, x4, y4);
982 if(!graphics || !pen)
983 return InvalidParameter;
985 if(graphics->busy)
986 return ObjectBusy;
988 pt[0].X = x1;
989 pt[0].Y = y1;
990 pt[1].X = x2;
991 pt[1].Y = y2;
992 pt[2].X = x3;
993 pt[2].Y = y3;
994 pt[3].X = x4;
995 pt[3].Y = y4;
997 save_state = prepare_dc(graphics, pen);
999 retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1001 restore_dc(graphics, save_state);
1003 return retval;
1006 GpStatus WINGDIPAPI GdipDrawBezierI(GpGraphics *graphics, GpPen *pen, INT x1,
1007 INT y1, INT x2, INT y2, INT x3, INT y3, INT x4, INT y4)
1009 INT save_state;
1010 GpPointF pt[4];
1011 GpStatus retval;
1013 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1014 x2, y2, x3, y3, x4, y4);
1016 if(!graphics || !pen)
1017 return InvalidParameter;
1019 if(graphics->busy)
1020 return ObjectBusy;
1022 pt[0].X = x1;
1023 pt[0].Y = y1;
1024 pt[1].X = x2;
1025 pt[1].Y = y2;
1026 pt[2].X = x3;
1027 pt[2].Y = y3;
1028 pt[3].X = x4;
1029 pt[3].Y = y4;
1031 save_state = prepare_dc(graphics, pen);
1033 retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1035 restore_dc(graphics, save_state);
1037 return retval;
1040 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1041 GDIPCONST GpPointF *points, INT count)
1043 INT i;
1044 GpStatus ret;
1046 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1048 if(!graphics || !pen || !points || (count <= 0))
1049 return InvalidParameter;
1051 if(graphics->busy)
1052 return ObjectBusy;
1054 for(i = 0; i < floor(count / 4); i++){
1055 ret = GdipDrawBezier(graphics, pen,
1056 points[4*i].X, points[4*i].Y,
1057 points[4*i + 1].X, points[4*i + 1].Y,
1058 points[4*i + 2].X, points[4*i + 2].Y,
1059 points[4*i + 3].X, points[4*i + 3].Y);
1060 if(ret != Ok)
1061 return ret;
1064 return Ok;
1067 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1068 GDIPCONST GpPoint *points, INT count)
1070 GpPointF *pts;
1071 GpStatus ret;
1072 INT i;
1074 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1076 if(!graphics || !pen || !points || (count <= 0))
1077 return InvalidParameter;
1079 if(graphics->busy)
1080 return ObjectBusy;
1082 pts = GdipAlloc(sizeof(GpPointF) * count);
1083 if(!pts)
1084 return OutOfMemory;
1086 for(i = 0; i < count; i++){
1087 pts[i].X = (REAL)points[i].X;
1088 pts[i].Y = (REAL)points[i].Y;
1091 ret = GdipDrawBeziers(graphics,pen,pts,count);
1093 GdipFree(pts);
1095 return ret;
1098 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1099 GDIPCONST GpPointF *points, INT count)
1101 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1103 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1106 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1107 GDIPCONST GpPoint *points, INT count)
1109 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1111 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1114 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1115 GDIPCONST GpPointF *points, INT count, REAL tension)
1117 GpPath *path;
1118 GpStatus stat;
1120 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1122 if(!graphics || !pen || !points || count <= 0)
1123 return InvalidParameter;
1125 if(graphics->busy)
1126 return ObjectBusy;
1128 if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1129 return stat;
1131 stat = GdipAddPathClosedCurve2(path, points, count, tension);
1132 if(stat != Ok){
1133 GdipDeletePath(path);
1134 return stat;
1137 stat = GdipDrawPath(graphics, pen, path);
1139 GdipDeletePath(path);
1141 return stat;
1144 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1145 GDIPCONST GpPoint *points, INT count, REAL tension)
1147 GpPointF *ptf;
1148 GpStatus stat;
1149 INT i;
1151 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1153 if(!points || count <= 0)
1154 return InvalidParameter;
1156 ptf = GdipAlloc(sizeof(GpPointF)*count);
1157 if(!ptf)
1158 return OutOfMemory;
1160 for(i = 0; i < count; i++){
1161 ptf[i].X = (REAL)points[i].X;
1162 ptf[i].Y = (REAL)points[i].Y;
1165 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1167 GdipFree(ptf);
1169 return stat;
1172 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1173 GDIPCONST GpPointF *points, INT count)
1175 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1177 return GdipDrawCurve2(graphics,pen,points,count,1.0);
1180 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1181 GDIPCONST GpPoint *points, INT count)
1183 GpPointF *pointsF;
1184 GpStatus ret;
1185 INT i;
1187 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1189 if(!points || count <= 0)
1190 return InvalidParameter;
1192 pointsF = GdipAlloc(sizeof(GpPointF)*count);
1193 if(!pointsF)
1194 return OutOfMemory;
1196 for(i = 0; i < count; i++){
1197 pointsF[i].X = (REAL)points[i].X;
1198 pointsF[i].Y = (REAL)points[i].Y;
1201 ret = GdipDrawCurve(graphics,pen,pointsF,count);
1202 GdipFree(pointsF);
1204 return ret;
1207 /* Approximates cardinal spline with Bezier curves. */
1208 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1209 GDIPCONST GpPointF *points, INT count, REAL tension)
1211 /* PolyBezier expects count*3-2 points. */
1212 INT i, len_pt = count*3-2, save_state;
1213 GpPointF *pt;
1214 REAL x1, x2, y1, y2;
1215 GpStatus retval;
1217 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1219 if(!graphics || !pen)
1220 return InvalidParameter;
1222 if(graphics->busy)
1223 return ObjectBusy;
1225 pt = GdipAlloc(len_pt * sizeof(GpPointF));
1226 tension = tension * TENSION_CONST;
1228 calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1229 tension, &x1, &y1);
1231 pt[0].X = points[0].X;
1232 pt[0].Y = points[0].Y;
1233 pt[1].X = x1;
1234 pt[1].Y = y1;
1236 for(i = 0; i < count-2; i++){
1237 calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1239 pt[3*i+2].X = x1;
1240 pt[3*i+2].Y = y1;
1241 pt[3*i+3].X = points[i+1].X;
1242 pt[3*i+3].Y = points[i+1].Y;
1243 pt[3*i+4].X = x2;
1244 pt[3*i+4].Y = y2;
1247 calc_curve_bezier_endp(points[count-1].X, points[count-1].Y,
1248 points[count-2].X, points[count-2].Y, tension, &x1, &y1);
1250 pt[len_pt-2].X = x1;
1251 pt[len_pt-2].Y = y1;
1252 pt[len_pt-1].X = points[count-1].X;
1253 pt[len_pt-1].Y = points[count-1].Y;
1255 save_state = prepare_dc(graphics, pen);
1257 retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1259 GdipFree(pt);
1260 restore_dc(graphics, save_state);
1262 return retval;
1265 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1266 GDIPCONST GpPoint *points, INT count, REAL tension)
1268 GpPointF *pointsF;
1269 GpStatus ret;
1270 INT i;
1272 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1274 if(!points || count <= 0)
1275 return InvalidParameter;
1277 pointsF = GdipAlloc(sizeof(GpPointF)*count);
1278 if(!pointsF)
1279 return OutOfMemory;
1281 for(i = 0; i < count; i++){
1282 pointsF[i].X = (REAL)points[i].X;
1283 pointsF[i].Y = (REAL)points[i].Y;
1286 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1287 GdipFree(pointsF);
1289 return ret;
1292 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1293 REAL y, REAL width, REAL height)
1295 INT save_state;
1296 GpPointF ptf[2];
1297 POINT pti[2];
1299 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1301 if(!graphics || !pen)
1302 return InvalidParameter;
1304 if(graphics->busy)
1305 return ObjectBusy;
1307 ptf[0].X = x;
1308 ptf[0].Y = y;
1309 ptf[1].X = x + width;
1310 ptf[1].Y = y + height;
1312 save_state = prepare_dc(graphics, pen);
1313 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1315 transform_and_round_points(graphics, pti, ptf, 2);
1317 Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1319 restore_dc(graphics, save_state);
1321 return Ok;
1324 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1325 INT y, INT width, INT height)
1327 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1329 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1333 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1335 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1337 /* IPicture::Render uses LONG coords */
1338 return GdipDrawImageI(graphics,image,roundr(x),roundr(y));
1341 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1342 INT y)
1344 UINT width, height, srcw, srch;
1346 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1348 if(!graphics || !image)
1349 return InvalidParameter;
1351 GdipGetImageWidth(image, &width);
1352 GdipGetImageHeight(image, &height);
1354 srcw = width * (((REAL) INCH_HIMETRIC) /
1355 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1356 srch = height * (((REAL) INCH_HIMETRIC) /
1357 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1359 if(image->type != ImageTypeMetafile){
1360 y += height;
1361 height *= -1;
1364 IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1365 0, 0, srcw, srch, NULL);
1367 return Ok;
1370 /* FIXME: partially implemented (only works for rectangular parallelograms) */
1371 GpStatus WINGDIPAPI GdipDrawImagePointsRect(GpGraphics *graphics, GpImage *image,
1372 GDIPCONST GpPointF *points, INT count, REAL srcx, REAL srcy, REAL srcwidth,
1373 REAL srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1374 DrawImageAbort callback, VOID * callbackData)
1376 GpPointF ptf[3];
1377 POINT pti[3];
1378 REAL dx, dy;
1380 TRACE("(%p, %p, %p, %d, %f, %f, %f, %f, %d, %p, %p, %p)\n", graphics, image, points,
1381 count, srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1382 callbackData);
1384 if(!graphics || !image || !points || count != 3)
1385 return InvalidParameter;
1387 if(srcUnit == UnitInch)
1388 dx = dy = (REAL) INCH_HIMETRIC;
1389 else if(srcUnit == UnitPixel){
1390 dx = ((REAL) INCH_HIMETRIC) /
1391 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX));
1392 dy = ((REAL) INCH_HIMETRIC) /
1393 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY));
1395 else
1396 return NotImplemented;
1398 memcpy(ptf, points, 3 * sizeof(GpPointF));
1399 transform_and_round_points(graphics, pti, ptf, 3);
1401 /* IPicture renders bitmaps with the y-axis reversed
1402 * FIXME: flipping for unknown image type might not be correct. */
1403 if(image->type != ImageTypeMetafile){
1404 INT temp;
1405 temp = pti[0].y;
1406 pti[0].y = pti[2].y;
1407 pti[2].y = temp;
1410 if(IPicture_Render(image->picture, graphics->hdc,
1411 pti[0].x, pti[0].y, pti[1].x - pti[0].x, pti[2].y - pti[0].y,
1412 srcx * dx, srcy * dy,
1413 srcwidth * dx, srcheight * dy,
1414 NULL) != S_OK){
1415 if(callback)
1416 callback(callbackData);
1417 return GenericError;
1420 return Ok;
1423 GpStatus WINGDIPAPI GdipDrawImagePointsRectI(GpGraphics *graphics, GpImage *image,
1424 GDIPCONST GpPoint *points, INT count, INT srcx, INT srcy, INT srcwidth,
1425 INT srcheight, GpUnit srcUnit, GDIPCONST GpImageAttributes* imageAttributes,
1426 DrawImageAbort callback, VOID * callbackData)
1428 GpPointF pointsF[3];
1429 INT i;
1431 TRACE("(%p, %p, %p, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n", graphics, image, points, count,
1432 srcx, srcy, srcwidth, srcheight, srcUnit, imageAttributes, callback,
1433 callbackData);
1435 if(!points || count!=3)
1436 return InvalidParameter;
1438 for(i = 0; i < count; i++){
1439 pointsF[i].X = (REAL)points[i].X;
1440 pointsF[i].Y = (REAL)points[i].Y;
1443 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
1444 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
1445 callback, callbackData);
1448 GpStatus WINGDIPAPI GdipDrawImageRectRect(GpGraphics *graphics, GpImage *image,
1449 REAL dstx, REAL dsty, REAL dstwidth, REAL dstheight, REAL srcx, REAL srcy,
1450 REAL srcwidth, REAL srcheight, GpUnit srcUnit,
1451 GDIPCONST GpImageAttributes* imageattr, DrawImageAbort callback,
1452 VOID * callbackData)
1454 GpPointF points[3];
1456 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p, %p, %p)\n",
1457 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1458 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1460 points[0].X = dstx;
1461 points[0].Y = dsty;
1462 points[1].X = dstx + dstwidth;
1463 points[1].Y = dsty;
1464 points[2].X = dstx;
1465 points[2].Y = dsty + dstheight;
1467 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1468 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1471 GpStatus WINGDIPAPI GdipDrawImageRectRectI(GpGraphics *graphics, GpImage *image,
1472 INT dstx, INT dsty, INT dstwidth, INT dstheight, INT srcx, INT srcy,
1473 INT srcwidth, INT srcheight, GpUnit srcUnit,
1474 GDIPCONST GpImageAttributes* imageAttributes, DrawImageAbort callback,
1475 VOID * callbackData)
1477 GpPointF points[3];
1479 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %p, %p, %p)\n",
1480 graphics, image, dstx, dsty, dstwidth, dstheight, srcx, srcy,
1481 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1483 points[0].X = dstx;
1484 points[0].Y = dsty;
1485 points[1].X = dstx + dstwidth;
1486 points[1].Y = dsty;
1487 points[2].X = dstx;
1488 points[2].Y = dsty + dstheight;
1490 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1491 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1494 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
1495 REAL x, REAL y, REAL width, REAL height)
1497 RectF bounds;
1498 GpUnit unit;
1499 GpStatus ret;
1501 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
1503 if(!graphics || !image)
1504 return InvalidParameter;
1506 ret = GdipGetImageBounds(image, &bounds, &unit);
1507 if(ret != Ok)
1508 return ret;
1510 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
1511 bounds.X, bounds.Y, bounds.Width, bounds.Height,
1512 unit, NULL, NULL, NULL);
1515 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
1516 INT x, INT y, INT width, INT height)
1518 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
1520 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
1523 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1524 REAL y1, REAL x2, REAL y2)
1526 INT save_state;
1527 GpPointF pt[2];
1528 GpStatus retval;
1530 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
1532 if(!pen || !graphics)
1533 return InvalidParameter;
1535 if(graphics->busy)
1536 return ObjectBusy;
1538 pt[0].X = x1;
1539 pt[0].Y = y1;
1540 pt[1].X = x2;
1541 pt[1].Y = y2;
1543 save_state = prepare_dc(graphics, pen);
1545 retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1547 restore_dc(graphics, save_state);
1549 return retval;
1552 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1553 INT y1, INT x2, INT y2)
1555 INT save_state;
1556 GpPointF pt[2];
1557 GpStatus retval;
1559 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
1561 if(!pen || !graphics)
1562 return InvalidParameter;
1564 if(graphics->busy)
1565 return ObjectBusy;
1567 pt[0].X = (REAL)x1;
1568 pt[0].Y = (REAL)y1;
1569 pt[1].X = (REAL)x2;
1570 pt[1].Y = (REAL)y2;
1572 save_state = prepare_dc(graphics, pen);
1574 retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1576 restore_dc(graphics, save_state);
1578 return retval;
1581 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1582 GpPointF *points, INT count)
1584 INT save_state;
1585 GpStatus retval;
1587 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1589 if(!pen || !graphics || (count < 2))
1590 return InvalidParameter;
1592 if(graphics->busy)
1593 return ObjectBusy;
1595 save_state = prepare_dc(graphics, pen);
1597 retval = draw_polyline(graphics, pen, points, count, TRUE);
1599 restore_dc(graphics, save_state);
1601 return retval;
1604 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
1605 GpPoint *points, INT count)
1607 INT save_state;
1608 GpStatus retval;
1609 GpPointF *ptf = NULL;
1610 int i;
1612 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1614 if(!pen || !graphics || (count < 2))
1615 return InvalidParameter;
1617 if(graphics->busy)
1618 return ObjectBusy;
1620 ptf = GdipAlloc(count * sizeof(GpPointF));
1621 if(!ptf) return OutOfMemory;
1623 for(i = 0; i < count; i ++){
1624 ptf[i].X = (REAL) points[i].X;
1625 ptf[i].Y = (REAL) points[i].Y;
1628 save_state = prepare_dc(graphics, pen);
1630 retval = draw_polyline(graphics, pen, ptf, count, TRUE);
1632 restore_dc(graphics, save_state);
1634 GdipFree(ptf);
1635 return retval;
1638 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1640 INT save_state;
1641 GpStatus retval;
1643 TRACE("(%p, %p, %p)\n", graphics, pen, path);
1645 if(!pen || !graphics)
1646 return InvalidParameter;
1648 if(graphics->busy)
1649 return ObjectBusy;
1651 save_state = prepare_dc(graphics, pen);
1653 retval = draw_poly(graphics, pen, path->pathdata.Points,
1654 path->pathdata.Types, path->pathdata.Count, TRUE);
1656 restore_dc(graphics, save_state);
1658 return retval;
1661 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1662 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1664 INT save_state;
1666 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1667 width, height, startAngle, sweepAngle);
1669 if(!graphics || !pen)
1670 return InvalidParameter;
1672 if(graphics->busy)
1673 return ObjectBusy;
1675 save_state = prepare_dc(graphics, pen);
1676 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1678 draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1680 restore_dc(graphics, save_state);
1682 return Ok;
1685 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
1686 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1688 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1689 width, height, startAngle, sweepAngle);
1691 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1694 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
1695 REAL y, REAL width, REAL height)
1697 INT save_state;
1698 GpPointF ptf[4];
1699 POINT pti[4];
1701 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1703 if(!pen || !graphics)
1704 return InvalidParameter;
1706 if(graphics->busy)
1707 return ObjectBusy;
1709 ptf[0].X = x;
1710 ptf[0].Y = y;
1711 ptf[1].X = x + width;
1712 ptf[1].Y = y;
1713 ptf[2].X = x + width;
1714 ptf[2].Y = y + height;
1715 ptf[3].X = x;
1716 ptf[3].Y = y + height;
1718 save_state = prepare_dc(graphics, pen);
1719 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1721 transform_and_round_points(graphics, pti, ptf, 4);
1722 Polygon(graphics->hdc, pti, 4);
1724 restore_dc(graphics, save_state);
1726 return Ok;
1729 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
1730 INT y, INT width, INT height)
1732 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1734 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1737 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
1738 GDIPCONST GpRectF* rects, INT count)
1740 GpPointF *ptf;
1741 POINT *pti;
1742 INT save_state, i;
1744 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1746 if(!graphics || !pen || !rects || count < 1)
1747 return InvalidParameter;
1749 if(graphics->busy)
1750 return ObjectBusy;
1752 ptf = GdipAlloc(4 * count * sizeof(GpPointF));
1753 pti = GdipAlloc(4 * count * sizeof(POINT));
1755 if(!ptf || !pti){
1756 GdipFree(ptf);
1757 GdipFree(pti);
1758 return OutOfMemory;
1761 for(i = 0; i < count; i++){
1762 ptf[4 * i + 3].X = ptf[4 * i].X = rects[i].X;
1763 ptf[4 * i + 1].Y = ptf[4 * i].Y = rects[i].Y;
1764 ptf[4 * i + 2].X = ptf[4 * i + 1].X = rects[i].X + rects[i].Width;
1765 ptf[4 * i + 3].Y = ptf[4 * i + 2].Y = rects[i].Y + rects[i].Height;
1768 save_state = prepare_dc(graphics, pen);
1769 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1771 transform_and_round_points(graphics, pti, ptf, 4 * count);
1773 for(i = 0; i < count; i++)
1774 Polygon(graphics->hdc, &pti[4 * i], 4);
1776 restore_dc(graphics, save_state);
1778 GdipFree(ptf);
1779 GdipFree(pti);
1781 return Ok;
1784 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
1785 GDIPCONST GpRect* rects, INT count)
1787 GpRectF *rectsF;
1788 GpStatus ret;
1789 INT i;
1791 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1793 if(!rects || count<=0)
1794 return InvalidParameter;
1796 rectsF = GdipAlloc(sizeof(GpRectF) * count);
1797 if(!rectsF)
1798 return OutOfMemory;
1800 for(i = 0;i < count;i++){
1801 rectsF[i].X = (REAL)rects[i].X;
1802 rectsF[i].Y = (REAL)rects[i].Y;
1803 rectsF[i].Width = (REAL)rects[i].Width;
1804 rectsF[i].Height = (REAL)rects[i].Height;
1807 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
1808 GdipFree(rectsF);
1810 return ret;
1813 GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string,
1814 INT length, GDIPCONST GpFont *font, GDIPCONST RectF *rect,
1815 GDIPCONST GpStringFormat *format, GDIPCONST GpBrush *brush)
1817 HRGN rgn = NULL;
1818 HFONT gdifont;
1819 LOGFONTW lfw;
1820 TEXTMETRICW textmet;
1821 GpPointF pt[2], rectcpy[4];
1822 POINT corners[4];
1823 WCHAR* stringdup;
1824 REAL angle, ang_cos, ang_sin, rel_width, rel_height;
1825 INT sum = 0, height = 0, fit, fitcpy, save_state, i, j, lret, nwidth,
1826 nheight;
1827 SIZE size;
1828 RECT drawcoord;
1830 if(!graphics || !string || !font || !brush || !rect)
1831 return InvalidParameter;
1833 if((brush->bt != BrushTypeSolidColor)){
1834 FIXME("not implemented for given parameters\n");
1835 return NotImplemented;
1838 if(format)
1839 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
1841 if(length == -1) length = lstrlenW(string);
1843 stringdup = GdipAlloc(length * sizeof(WCHAR));
1844 if(!stringdup) return OutOfMemory;
1846 save_state = SaveDC(graphics->hdc);
1847 SetBkMode(graphics->hdc, TRANSPARENT);
1848 SetTextColor(graphics->hdc, brush->lb.lbColor);
1850 rectcpy[3].X = rectcpy[0].X = rect->X;
1851 rectcpy[1].Y = rectcpy[0].Y = rect->Y;
1852 rectcpy[2].X = rectcpy[1].X = rect->X + rect->Width;
1853 rectcpy[3].Y = rectcpy[2].Y = rect->Y + rect->Height;
1854 transform_and_round_points(graphics, corners, rectcpy, 4);
1856 if(roundr(rect->Width) == 0 && roundr(rect->Height) == 0){
1857 rel_width = rel_height = 1.0;
1858 nwidth = nheight = INT_MAX;
1860 else{
1861 rel_width = sqrt((corners[1].x - corners[0].x) * (corners[1].x - corners[0].x) +
1862 (corners[1].y - corners[0].y) * (corners[1].y - corners[0].y))
1863 / rect->Width;
1864 rel_height = sqrt((corners[2].x - corners[1].x) * (corners[2].x - corners[1].x) +
1865 (corners[2].y - corners[1].y) * (corners[2].y - corners[1].y))
1866 / rect->Height;
1868 nwidth = roundr(rel_width * rect->Width);
1869 nheight = roundr(rel_height * rect->Height);
1870 rgn = CreatePolygonRgn(corners, 4, ALTERNATE);
1871 SelectClipRgn(graphics->hdc, rgn);
1874 /* Use gdi to find the font, then perform transformations on it (height,
1875 * width, angle). */
1876 SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
1877 GetTextMetricsW(graphics->hdc, &textmet);
1878 lfw = font->lfw;
1880 lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
1881 lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
1883 pt[0].X = 0.0;
1884 pt[0].Y = 0.0;
1885 pt[1].X = 1.0;
1886 pt[1].Y = 0.0;
1887 GdipTransformMatrixPoints(graphics->worldtrans, pt, 2);
1888 angle = gdiplus_atan2((pt[1].Y - pt[0].Y), (pt[1].X - pt[0].X));
1889 ang_cos = cos(angle);
1890 ang_sin = sin(angle);
1891 lfw.lfEscapement = lfw.lfOrientation = -roundr((angle / M_PI) * 1800.0);
1893 gdifont = CreateFontIndirectW(&lfw);
1894 DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
1896 for(i = 0, j = 0; i < length; i++){
1897 if(!isprintW(string[i]) && (string[i] != '\n'))
1898 continue;
1900 stringdup[j] = string[i];
1901 j++;
1904 stringdup[j] = 0;
1905 length = j;
1907 while(sum < length){
1908 drawcoord.left = corners[0].x + roundr(ang_sin * (REAL) height);
1909 drawcoord.top = corners[0].y + roundr(ang_cos * (REAL) height);
1911 GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
1912 nwidth, &fit, NULL, &size);
1913 fitcpy = fit;
1915 if(fit == 0){
1916 DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, DT_NOCLIP |
1917 DT_EXPANDTABS);
1918 break;
1921 for(lret = 0; lret < fit; lret++)
1922 if(*(stringdup + sum + lret) == '\n')
1923 break;
1925 /* Line break code (may look strange, but it imitates windows). */
1926 if(lret < fit)
1927 fit = lret; /* this is not an off-by-one error */
1928 else if(fit < (length - sum)){
1929 if(*(stringdup + sum + fit) == ' ')
1930 while(*(stringdup + sum + fit) == ' ')
1931 fit++;
1932 else
1933 while(*(stringdup + sum + fit - 1) != ' '){
1934 fit--;
1936 if(*(stringdup + sum + fit) == '\t')
1937 break;
1939 if(fit == 0){
1940 fit = fitcpy;
1941 break;
1945 DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
1946 &drawcoord, DT_NOCLIP | DT_EXPANDTABS);
1948 sum += fit + (lret < fitcpy ? 1 : 0);
1949 height += size.cy;
1951 if(height > nheight)
1952 break;
1954 /* Stop if this was a linewrap (but not if it was a linebreak). */
1955 if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
1956 break;
1959 GdipFree(stringdup);
1960 DeleteObject(rgn);
1961 DeleteObject(gdifont);
1963 RestoreDC(graphics->hdc, save_state);
1965 return Ok;
1968 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
1969 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
1971 GpPath *path;
1972 GpStatus stat;
1974 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
1975 count, tension, fill);
1977 if(!graphics || !brush || !points)
1978 return InvalidParameter;
1980 if(graphics->busy)
1981 return ObjectBusy;
1983 stat = GdipCreatePath(fill, &path);
1984 if(stat != Ok)
1985 return stat;
1987 stat = GdipAddPathClosedCurve2(path, points, count, tension);
1988 if(stat != Ok){
1989 GdipDeletePath(path);
1990 return stat;
1993 stat = GdipFillPath(graphics, brush, path);
1994 if(stat != Ok){
1995 GdipDeletePath(path);
1996 return stat;
1999 GdipDeletePath(path);
2001 return Ok;
2004 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2005 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2007 GpPointF *ptf;
2008 GpStatus stat;
2009 INT i;
2011 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2012 count, tension, fill);
2014 if(!points || count <= 0)
2015 return InvalidParameter;
2017 ptf = GdipAlloc(sizeof(GpPointF)*count);
2018 if(!ptf)
2019 return OutOfMemory;
2021 for(i = 0;i < count;i++){
2022 ptf[i].X = (REAL)points[i].X;
2023 ptf[i].Y = (REAL)points[i].Y;
2026 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2028 GdipFree(ptf);
2030 return stat;
2033 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2034 REAL y, REAL width, REAL height)
2036 INT save_state;
2037 GpPointF ptf[2];
2038 POINT pti[2];
2040 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2042 if(!graphics || !brush)
2043 return InvalidParameter;
2045 if(graphics->busy)
2046 return ObjectBusy;
2048 ptf[0].X = x;
2049 ptf[0].Y = y;
2050 ptf[1].X = x + width;
2051 ptf[1].Y = y + height;
2053 save_state = SaveDC(graphics->hdc);
2054 EndPath(graphics->hdc);
2055 SelectObject(graphics->hdc, brush->gdibrush);
2056 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2058 transform_and_round_points(graphics, pti, ptf, 2);
2060 Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2062 RestoreDC(graphics->hdc, save_state);
2064 return Ok;
2067 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2068 INT y, INT width, INT height)
2070 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2072 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2075 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2077 INT save_state;
2078 GpStatus retval;
2080 TRACE("(%p, %p, %p)\n", graphics, brush, path);
2082 if(!brush || !graphics || !path)
2083 return InvalidParameter;
2085 if(graphics->busy)
2086 return ObjectBusy;
2088 save_state = SaveDC(graphics->hdc);
2089 EndPath(graphics->hdc);
2090 SelectObject(graphics->hdc, brush->gdibrush);
2091 SetPolyFillMode(graphics->hdc, (path->fill == FillModeAlternate ? ALTERNATE
2092 : WINDING));
2094 BeginPath(graphics->hdc);
2095 retval = draw_poly(graphics, NULL, path->pathdata.Points,
2096 path->pathdata.Types, path->pathdata.Count, FALSE);
2098 if(retval != Ok)
2099 goto end;
2101 EndPath(graphics->hdc);
2102 FillPath(graphics->hdc);
2104 retval = Ok;
2106 end:
2107 RestoreDC(graphics->hdc, save_state);
2109 return retval;
2112 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2113 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2115 INT save_state;
2117 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2118 graphics, brush, x, y, width, height, startAngle, sweepAngle);
2120 if(!graphics || !brush)
2121 return InvalidParameter;
2123 if(graphics->busy)
2124 return ObjectBusy;
2126 save_state = SaveDC(graphics->hdc);
2127 EndPath(graphics->hdc);
2128 SelectObject(graphics->hdc, brush->gdibrush);
2129 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2131 draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2133 RestoreDC(graphics->hdc, save_state);
2135 return Ok;
2138 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2139 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2141 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2142 graphics, brush, x, y, width, height, startAngle, sweepAngle);
2144 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2147 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2148 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2150 INT save_state;
2151 GpPointF *ptf = NULL;
2152 POINT *pti = NULL;
2153 GpStatus retval = Ok;
2155 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2157 if(!graphics || !brush || !points || !count)
2158 return InvalidParameter;
2160 if(graphics->busy)
2161 return ObjectBusy;
2163 ptf = GdipAlloc(count * sizeof(GpPointF));
2164 pti = GdipAlloc(count * sizeof(POINT));
2165 if(!ptf || !pti){
2166 retval = OutOfMemory;
2167 goto end;
2170 memcpy(ptf, points, count * sizeof(GpPointF));
2172 save_state = SaveDC(graphics->hdc);
2173 EndPath(graphics->hdc);
2174 SelectObject(graphics->hdc, brush->gdibrush);
2175 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2176 SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2177 : WINDING));
2179 transform_and_round_points(graphics, pti, ptf, count);
2180 Polygon(graphics->hdc, pti, count);
2182 RestoreDC(graphics->hdc, save_state);
2184 end:
2185 GdipFree(ptf);
2186 GdipFree(pti);
2188 return retval;
2191 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2192 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2194 INT save_state, i;
2195 GpPointF *ptf = NULL;
2196 POINT *pti = NULL;
2197 GpStatus retval = Ok;
2199 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2201 if(!graphics || !brush || !points || !count)
2202 return InvalidParameter;
2204 if(graphics->busy)
2205 return ObjectBusy;
2207 ptf = GdipAlloc(count * sizeof(GpPointF));
2208 pti = GdipAlloc(count * sizeof(POINT));
2209 if(!ptf || !pti){
2210 retval = OutOfMemory;
2211 goto end;
2214 for(i = 0; i < count; i ++){
2215 ptf[i].X = (REAL) points[i].X;
2216 ptf[i].Y = (REAL) points[i].Y;
2219 save_state = SaveDC(graphics->hdc);
2220 EndPath(graphics->hdc);
2221 SelectObject(graphics->hdc, brush->gdibrush);
2222 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2223 SetPolyFillMode(graphics->hdc, (fillMode == FillModeAlternate ? ALTERNATE
2224 : WINDING));
2226 transform_and_round_points(graphics, pti, ptf, count);
2227 Polygon(graphics->hdc, pti, count);
2229 RestoreDC(graphics->hdc, save_state);
2231 end:
2232 GdipFree(ptf);
2233 GdipFree(pti);
2235 return retval;
2238 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2239 GDIPCONST GpPointF *points, INT count)
2241 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2243 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2246 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2247 GDIPCONST GpPoint *points, INT count)
2249 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2251 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2254 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2255 REAL x, REAL y, REAL width, REAL height)
2257 INT save_state;
2258 GpPointF ptf[4];
2259 POINT pti[4];
2261 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2263 if(!graphics || !brush)
2264 return InvalidParameter;
2266 if(graphics->busy)
2267 return ObjectBusy;
2269 ptf[0].X = x;
2270 ptf[0].Y = y;
2271 ptf[1].X = x + width;
2272 ptf[1].Y = y;
2273 ptf[2].X = x + width;
2274 ptf[2].Y = y + height;
2275 ptf[3].X = x;
2276 ptf[3].Y = y + height;
2278 save_state = SaveDC(graphics->hdc);
2279 EndPath(graphics->hdc);
2280 SelectObject(graphics->hdc, brush->gdibrush);
2281 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2283 transform_and_round_points(graphics, pti, ptf, 4);
2285 Polygon(graphics->hdc, pti, 4);
2287 RestoreDC(graphics->hdc, save_state);
2289 return Ok;
2292 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2293 INT x, INT y, INT width, INT height)
2295 INT save_state;
2296 GpPointF ptf[4];
2297 POINT pti[4];
2299 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2301 if(!graphics || !brush)
2302 return InvalidParameter;
2304 if(graphics->busy)
2305 return ObjectBusy;
2307 ptf[0].X = x;
2308 ptf[0].Y = y;
2309 ptf[1].X = x + width;
2310 ptf[1].Y = y;
2311 ptf[2].X = x + width;
2312 ptf[2].Y = y + height;
2313 ptf[3].X = x;
2314 ptf[3].Y = y + height;
2316 save_state = SaveDC(graphics->hdc);
2317 EndPath(graphics->hdc);
2318 SelectObject(graphics->hdc, brush->gdibrush);
2319 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2321 transform_and_round_points(graphics, pti, ptf, 4);
2323 Polygon(graphics->hdc, pti, 4);
2325 RestoreDC(graphics->hdc, save_state);
2327 return Ok;
2330 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2331 INT count)
2333 GpStatus ret;
2334 INT i;
2336 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2338 if(!rects)
2339 return InvalidParameter;
2341 for(i = 0; i < count; i++){
2342 ret = GdipFillRectangle(graphics, brush, rects[i].X, rects[i].Y, rects[i].Width, rects[i].Height);
2343 if(ret != Ok) return ret;
2346 return Ok;
2349 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2350 INT count)
2352 GpRectF *rectsF;
2353 GpStatus ret;
2354 INT i;
2356 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2358 if(!rects || count <= 0)
2359 return InvalidParameter;
2361 rectsF = GdipAlloc(sizeof(GpRectF)*count);
2362 if(!rectsF)
2363 return OutOfMemory;
2365 for(i = 0; i < count; i++){
2366 rectsF[i].X = (REAL)rects[i].X;
2367 rectsF[i].Y = (REAL)rects[i].Y;
2368 rectsF[i].X = (REAL)rects[i].Width;
2369 rectsF[i].Height = (REAL)rects[i].Height;
2372 ret = GdipFillRectangles(graphics,brush,rectsF,count);
2373 GdipFree(rectsF);
2375 return ret;
2378 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2379 GpRegion* region)
2381 if (!(graphics && brush && region))
2382 return InvalidParameter;
2384 if(graphics->busy)
2385 return ObjectBusy;
2387 FIXME("(%p, %p, %p): stub\n", graphics, brush, region);
2389 return NotImplemented;
2392 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2394 static int calls;
2396 if(!graphics)
2397 return InvalidParameter;
2399 if(graphics->busy)
2400 return ObjectBusy;
2402 if(!(calls++))
2403 FIXME("not implemented\n");
2405 return NotImplemented;
2408 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2409 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2410 CompositingMode *mode)
2412 TRACE("(%p, %p)\n", graphics, mode);
2414 if(!graphics || !mode)
2415 return InvalidParameter;
2417 if(graphics->busy)
2418 return ObjectBusy;
2420 *mode = graphics->compmode;
2422 return Ok;
2425 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2426 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2427 CompositingQuality *quality)
2429 TRACE("(%p, %p)\n", graphics, quality);
2431 if(!graphics || !quality)
2432 return InvalidParameter;
2434 if(graphics->busy)
2435 return ObjectBusy;
2437 *quality = graphics->compqual;
2439 return Ok;
2442 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2443 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2444 InterpolationMode *mode)
2446 TRACE("(%p, %p)\n", graphics, mode);
2448 if(!graphics || !mode)
2449 return InvalidParameter;
2451 if(graphics->busy)
2452 return ObjectBusy;
2454 *mode = graphics->interpolation;
2456 return Ok;
2459 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2461 TRACE("(%p, %p)\n", graphics, scale);
2463 if(!graphics || !scale)
2464 return InvalidParameter;
2466 if(graphics->busy)
2467 return ObjectBusy;
2469 *scale = graphics->scale;
2471 return Ok;
2474 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2476 TRACE("(%p, %p)\n", graphics, unit);
2478 if(!graphics || !unit)
2479 return InvalidParameter;
2481 if(graphics->busy)
2482 return ObjectBusy;
2484 *unit = graphics->unit;
2486 return Ok;
2489 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2490 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2491 *mode)
2493 TRACE("(%p, %p)\n", graphics, mode);
2495 if(!graphics || !mode)
2496 return InvalidParameter;
2498 if(graphics->busy)
2499 return ObjectBusy;
2501 *mode = graphics->pixeloffset;
2503 return Ok;
2506 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2507 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2509 TRACE("(%p, %p)\n", graphics, mode);
2511 if(!graphics || !mode)
2512 return InvalidParameter;
2514 if(graphics->busy)
2515 return ObjectBusy;
2517 *mode = graphics->smoothing;
2519 return Ok;
2522 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2524 TRACE("(%p, %p)\n", graphics, contrast);
2526 if(!graphics || !contrast)
2527 return InvalidParameter;
2529 *contrast = graphics->textcontrast;
2531 return Ok;
2534 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2535 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2536 TextRenderingHint *hint)
2538 TRACE("(%p, %p)\n", graphics, hint);
2540 if(!graphics || !hint)
2541 return InvalidParameter;
2543 if(graphics->busy)
2544 return ObjectBusy;
2546 *hint = graphics->texthint;
2548 return Ok;
2551 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2553 TRACE("(%p, %p)\n", graphics, matrix);
2555 if(!graphics || !matrix)
2556 return InvalidParameter;
2558 if(graphics->busy)
2559 return ObjectBusy;
2561 *matrix = *graphics->worldtrans;
2562 return Ok;
2565 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2567 GpSolidFill *brush;
2568 GpStatus stat;
2569 RECT rect;
2571 TRACE("(%p, %x)\n", graphics, color);
2573 if(!graphics)
2574 return InvalidParameter;
2576 if(graphics->busy)
2577 return ObjectBusy;
2579 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2580 return stat;
2582 if(graphics->hwnd){
2583 if(!GetWindowRect(graphics->hwnd, &rect)){
2584 GdipDeleteBrush((GpBrush*)brush);
2585 return GenericError;
2588 GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right - rect.left),
2589 (REAL)(rect.bottom - rect.top));
2591 else
2592 GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2593 (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2595 GdipDeleteBrush((GpBrush*)brush);
2597 return Ok;
2600 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2602 TRACE("(%p, %p)\n", graphics, res);
2604 if(!graphics || !res)
2605 return InvalidParameter;
2607 return GdipIsEmptyRegion(graphics->clip, graphics, res);
2610 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2612 FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2614 if(!graphics || !result)
2615 return InvalidParameter;
2617 if(graphics->busy)
2618 return ObjectBusy;
2620 return NotImplemented;
2623 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2625 FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2627 if(!graphics || !result)
2628 return InvalidParameter;
2630 if(graphics->busy)
2631 return ObjectBusy;
2633 return NotImplemented;
2636 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2637 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2638 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2639 INT regionCount, GpRegion** regions)
2641 if (!(graphics && string && font && layoutRect && stringFormat && regions))
2642 return InvalidParameter;
2644 FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2645 length, font, layoutRect, stringFormat, regionCount, regions);
2647 return NotImplemented;
2650 /* Find the smallest rectangle that bounds the text when it is printed in rect
2651 * according to the format options listed in format. If rect has 0 width and
2652 * height, then just find the smallest rectangle that bounds the text when it's
2653 * printed at location (rect->X, rect-Y). */
2654 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2655 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2656 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2657 INT *codepointsfitted, INT *linesfilled)
2659 HFONT oldfont;
2660 WCHAR* stringdup;
2661 INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2662 nheight;
2663 SIZE size;
2665 if(!graphics || !string || !font || !rect)
2666 return InvalidParameter;
2668 if(linesfilled) *linesfilled = 0;
2669 if(codepointsfitted) *codepointsfitted = 0;
2671 if(format)
2672 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2674 if(length == -1) length = lstrlenW(string);
2676 stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2677 if(!stringdup) return OutOfMemory;
2679 oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2680 nwidth = roundr(rect->Width);
2681 nheight = roundr(rect->Height);
2683 if((nwidth == 0) && (nheight == 0))
2684 nwidth = nheight = INT_MAX;
2686 for(i = 0, j = 0; i < length; i++){
2687 if(!isprintW(string[i]) && (string[i] != '\n'))
2688 continue;
2690 stringdup[j] = string[i];
2691 j++;
2694 stringdup[j] = 0;
2695 length = j;
2697 while(sum < length){
2698 GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2699 nwidth, &fit, NULL, &size);
2700 fitcpy = fit;
2702 if(fit == 0)
2703 break;
2705 for(lret = 0; lret < fit; lret++)
2706 if(*(stringdup + sum + lret) == '\n')
2707 break;
2709 /* Line break code (may look strange, but it imitates windows). */
2710 if(lret < fit)
2711 fit = lret; /* this is not an off-by-one error */
2712 else if(fit < (length - sum)){
2713 if(*(stringdup + sum + fit) == ' ')
2714 while(*(stringdup + sum + fit) == ' ')
2715 fit++;
2716 else
2717 while(*(stringdup + sum + fit - 1) != ' '){
2718 fit--;
2720 if(*(stringdup + sum + fit) == '\t')
2721 break;
2723 if(fit == 0){
2724 fit = fitcpy;
2725 break;
2730 GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2731 nwidth, &j, NULL, &size);
2733 sum += fit + (lret < fitcpy ? 1 : 0);
2734 if(codepointsfitted) *codepointsfitted = sum;
2736 height += size.cy;
2737 if(linesfilled) *linesfilled += size.cy;
2738 max_width = max(max_width, size.cx);
2740 if(height > nheight)
2741 break;
2743 /* Stop if this was a linewrap (but not if it was a linebreak). */
2744 if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2745 break;
2748 bounds->X = rect->X;
2749 bounds->Y = rect->Y;
2750 bounds->Width = (REAL)max_width;
2751 bounds->Height = (REAL) min(height, nheight);
2753 GdipFree(stringdup);
2754 DeleteObject(SelectObject(graphics->hdc, oldfont));
2756 return Ok;
2759 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
2761 TRACE("(%p)\n", graphics);
2763 if(!graphics)
2764 return InvalidParameter;
2766 if(graphics->busy)
2767 return ObjectBusy;
2769 return GdipSetInfinite(graphics->clip);
2772 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
2774 TRACE("(%p)\n", graphics);
2776 if(!graphics)
2777 return InvalidParameter;
2779 if(graphics->busy)
2780 return ObjectBusy;
2782 graphics->worldtrans->matrix[0] = 1.0;
2783 graphics->worldtrans->matrix[1] = 0.0;
2784 graphics->worldtrans->matrix[2] = 0.0;
2785 graphics->worldtrans->matrix[3] = 1.0;
2786 graphics->worldtrans->matrix[4] = 0.0;
2787 graphics->worldtrans->matrix[5] = 0.0;
2789 return Ok;
2792 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
2794 static int calls;
2796 if(!graphics)
2797 return InvalidParameter;
2799 if(!(calls++))
2800 FIXME("graphics state not implemented\n");
2802 return Ok;
2805 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
2806 GpMatrixOrder order)
2808 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
2810 if(!graphics)
2811 return InvalidParameter;
2813 if(graphics->busy)
2814 return ObjectBusy;
2816 return GdipRotateMatrix(graphics->worldtrans, angle, order);
2819 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
2821 static int calls;
2823 if(!graphics || !state)
2824 return InvalidParameter;
2826 if(!(calls++))
2827 FIXME("graphics state not implemented\n");
2829 *state = 0xdeadbeef;
2830 return Ok;
2833 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
2835 FIXME("(%p, %p)\n", graphics, state);
2837 if(!graphics || !state)
2838 return InvalidParameter;
2840 *state = 0xdeadbeef;
2841 return Ok;
2844 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
2846 FIXME("(%p, 0x%x)\n", graphics, state);
2848 if(!graphics || !state)
2849 return InvalidParameter;
2851 return Ok;
2854 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
2855 REAL sy, GpMatrixOrder order)
2857 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
2859 if(!graphics)
2860 return InvalidParameter;
2862 if(graphics->busy)
2863 return ObjectBusy;
2865 return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
2868 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
2869 CombineMode mode)
2871 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
2873 if(!graphics || !srcgraphics)
2874 return InvalidParameter;
2876 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
2879 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
2880 CompositingMode mode)
2882 TRACE("(%p, %d)\n", graphics, mode);
2884 if(!graphics)
2885 return InvalidParameter;
2887 if(graphics->busy)
2888 return ObjectBusy;
2890 graphics->compmode = mode;
2892 return Ok;
2895 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
2896 CompositingQuality quality)
2898 TRACE("(%p, %d)\n", graphics, quality);
2900 if(!graphics)
2901 return InvalidParameter;
2903 if(graphics->busy)
2904 return ObjectBusy;
2906 graphics->compqual = quality;
2908 return Ok;
2911 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
2912 InterpolationMode mode)
2914 TRACE("(%p, %d)\n", graphics, mode);
2916 if(!graphics)
2917 return InvalidParameter;
2919 if(graphics->busy)
2920 return ObjectBusy;
2922 graphics->interpolation = mode;
2924 return Ok;
2927 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
2929 TRACE("(%p, %.2f)\n", graphics, scale);
2931 if(!graphics || (scale <= 0.0))
2932 return InvalidParameter;
2934 if(graphics->busy)
2935 return ObjectBusy;
2937 graphics->scale = scale;
2939 return Ok;
2942 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
2944 TRACE("(%p, %d)\n", graphics, unit);
2946 if(!graphics)
2947 return InvalidParameter;
2949 if(graphics->busy)
2950 return ObjectBusy;
2952 if(unit == UnitWorld)
2953 return InvalidParameter;
2955 graphics->unit = unit;
2957 return Ok;
2960 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2961 mode)
2963 TRACE("(%p, %d)\n", graphics, mode);
2965 if(!graphics)
2966 return InvalidParameter;
2968 if(graphics->busy)
2969 return ObjectBusy;
2971 graphics->pixeloffset = mode;
2973 return Ok;
2976 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
2978 TRACE("(%p, %d)\n", graphics, mode);
2980 if(!graphics)
2981 return InvalidParameter;
2983 if(graphics->busy)
2984 return ObjectBusy;
2986 graphics->smoothing = mode;
2988 return Ok;
2991 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
2993 TRACE("(%p, %d)\n", graphics, contrast);
2995 if(!graphics)
2996 return InvalidParameter;
2998 graphics->textcontrast = contrast;
3000 return Ok;
3003 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3004 TextRenderingHint hint)
3006 TRACE("(%p, %d)\n", graphics, hint);
3008 if(!graphics)
3009 return InvalidParameter;
3011 if(graphics->busy)
3012 return ObjectBusy;
3014 graphics->texthint = hint;
3016 return Ok;
3019 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3021 TRACE("(%p, %p)\n", graphics, matrix);
3023 if(!graphics || !matrix)
3024 return InvalidParameter;
3026 if(graphics->busy)
3027 return ObjectBusy;
3029 GdipDeleteMatrix(graphics->worldtrans);
3030 return GdipCloneMatrix(matrix, &graphics->worldtrans);
3033 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3034 REAL dy, GpMatrixOrder order)
3036 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3038 if(!graphics)
3039 return InvalidParameter;
3041 if(graphics->busy)
3042 return ObjectBusy;
3044 return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3047 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3049 TRACE("(%p, %p, %d)\n", graphics, path, mode);
3051 if(!graphics)
3052 return InvalidParameter;
3054 if(graphics->busy)
3055 return ObjectBusy;
3057 return GdipCombineRegionPath(graphics->clip, path, mode);
3060 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3061 REAL width, REAL height,
3062 CombineMode mode)
3064 GpRectF rect;
3066 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3068 if(!graphics)
3069 return InvalidParameter;
3071 if(graphics->busy)
3072 return ObjectBusy;
3074 rect.X = x;
3075 rect.Y = y;
3076 rect.Width = width;
3077 rect.Height = height;
3079 return GdipCombineRegionRect(graphics->clip, &rect, mode);
3082 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3083 INT width, INT height,
3084 CombineMode mode)
3086 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3088 if(!graphics)
3089 return InvalidParameter;
3091 if(graphics->busy)
3092 return ObjectBusy;
3094 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3097 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3098 CombineMode mode)
3100 TRACE("(%p, %p, %d)\n", graphics, region, mode);
3102 if(!graphics || !region)
3103 return InvalidParameter;
3105 if(graphics->busy)
3106 return ObjectBusy;
3108 return GdipCombineRegionRegion(graphics->clip, region, mode);
3111 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3112 UINT limitDpi)
3114 static int calls;
3116 if(!(calls++))
3117 FIXME("not implemented\n");
3119 return NotImplemented;
3122 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3123 INT count)
3125 INT save_state;
3126 POINT *pti;
3128 TRACE("(%p, %p, %d)\n", graphics, points, count);
3130 if(!graphics || !pen || count<=0)
3131 return InvalidParameter;
3133 if(graphics->busy)
3134 return ObjectBusy;
3136 pti = GdipAlloc(sizeof(POINT) * count);
3138 save_state = prepare_dc(graphics, pen);
3139 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3141 transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3142 Polygon(graphics->hdc, pti, count);
3144 restore_dc(graphics, save_state);
3145 GdipFree(pti);
3147 return Ok;
3150 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3151 INT count)
3153 GpStatus ret;
3154 GpPointF *ptf;
3155 INT i;
3157 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3159 if(count<=0) return InvalidParameter;
3160 ptf = GdipAlloc(sizeof(GpPointF) * count);
3162 for(i = 0;i < count; i++){
3163 ptf[i].X = (REAL)points[i].X;
3164 ptf[i].Y = (REAL)points[i].Y;
3167 ret = GdipDrawPolygon(graphics,pen,ptf,count);
3168 GdipFree(ptf);
3170 return ret;
3173 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3175 TRACE("(%p, %p)\n", graphics, dpi);
3177 if(!graphics || !dpi)
3178 return InvalidParameter;
3180 if(graphics->busy)
3181 return ObjectBusy;
3183 *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3185 return Ok;
3188 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3190 TRACE("(%p, %p)\n", graphics, dpi);
3192 if(!graphics || !dpi)
3193 return InvalidParameter;
3195 if(graphics->busy)
3196 return ObjectBusy;
3198 *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3200 return Ok;
3203 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3204 GpMatrixOrder order)
3206 GpMatrix m;
3207 GpStatus ret;
3209 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3211 if(!graphics || !matrix)
3212 return InvalidParameter;
3214 if(graphics->busy)
3215 return ObjectBusy;
3217 m = *(graphics->worldtrans);
3219 ret = GdipMultiplyMatrix(&m, (GpMatrix*)matrix, order);
3220 if(ret == Ok)
3221 *(graphics->worldtrans) = m;
3223 return ret;
3226 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3228 TRACE("(%p, %p)\n", graphics, hdc);
3230 if(!graphics || !hdc)
3231 return InvalidParameter;
3233 if(graphics->busy)
3234 return ObjectBusy;
3236 *hdc = graphics->hdc;
3237 graphics->busy = TRUE;
3239 return Ok;
3242 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3244 TRACE("(%p, %p)\n", graphics, hdc);
3246 if(!graphics)
3247 return InvalidParameter;
3249 if(graphics->hdc != hdc || !(graphics->busy))
3250 return InvalidParameter;
3252 graphics->busy = FALSE;
3254 return Ok;
3257 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3259 GpRegion *clip;
3260 GpStatus status;
3262 TRACE("(%p, %p)\n", graphics, region);
3264 if(!graphics || !region)
3265 return InvalidParameter;
3267 if(graphics->busy)
3268 return ObjectBusy;
3270 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3271 return status;
3273 /* free everything except root node and header */
3274 delete_element(&region->node);
3275 memcpy(region, clip, sizeof(GpRegion));
3277 return Ok;
3280 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3281 GpCoordinateSpace src_space, GpPointF *points, INT count)
3283 if(!graphics || !points || count <= 0)
3284 return InvalidParameter;
3286 if(graphics->busy)
3287 return ObjectBusy;
3289 FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3291 return NotImplemented;
3294 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3295 GpCoordinateSpace src_space, GpPoint *points, INT count)
3297 FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3299 return NotImplemented;
3302 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3304 FIXME("\n");
3306 return NULL;