- Sync gdiplus with Wine head
[reactos.git] / reactos / dll / win32 / 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
42 WINE_DEFAULT_DEBUG_CHANNEL(gdiplus);
43
44 /* looks-right constants */
45 #define ANCHOR_WIDTH (2.0)
46 #define MAX_ITERS (50)
47
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)
50 {
51 REAL radAngle, hypotenuse;
52
53 radAngle = deg2rad(angle);
54 hypotenuse = 50.0; /* arbitrary */
55
56 *x = x_0 + cos(radAngle) * hypotenuse;
57 *y = y_0 + sin(radAngle) * hypotenuse;
58 }
59
60 /* Converts from gdiplus path point type to gdi path point type. */
61 static BYTE convert_path_point_type(BYTE type)
62 {
63 BYTE ret;
64
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;
78 }
79
80 if(type & PathPointTypeCloseSubpath)
81 ret |= PT_CLOSEFIGURE;
82
83 return ret;
84 }
85
86 static INT prepare_dc(GpGraphics *graphics, GpPen *pen)
87 {
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];
93
94 EndPath(graphics->hdc);
95
96 if(pen->unit == UnitPixel){
97 width = pen->width;
98 }
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);
109
110 width *= pen->width * convert_unit(graphics->hdc,
111 pen->unit == UnitWorld ? graphics->unit : pen->unit);
112 }
113
114 if(pen->dash == DashStyleCustom){
115 numdashes = min(pen->numdashes, MAX_DASHLEN);
116
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]);
121 }
122 TRACE("\n and the pen style is %x\n", pen->style);
123
124 gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb,
125 numdashes, dash_array);
126 }
127 else
128 gdipen = ExtCreatePen(pen->style, roundr(width), &pen->brush->lb, 0, NULL);
129
130 SelectObject(graphics->hdc, gdipen);
131
132 return save_state;
133 }
134
135 static void restore_dc(GpGraphics *graphics, INT state)
136 {
137 DeleteObject(SelectObject(graphics->hdc, GetStockObject(NULL_PEN)));
138 RestoreDC(graphics->hdc, state);
139 }
140
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.
150 */
151 static void transform_and_round_points(GpGraphics *graphics, POINT *pti,
152 GpPointF *ptf, INT count)
153 {
154 REAL unitscale;
155 GpMatrix *matrix;
156 int i;
157
158 unitscale = convert_unit(graphics->hdc, graphics->unit);
159
160 /* apply page scale */
161 if(graphics->unit != UnitDisplay)
162 unitscale *= graphics->scale;
163
164 GdipCloneMatrix(graphics->worldtrans, &matrix);
165 GdipScaleMatrix(matrix, unitscale, unitscale, MatrixOrderAppend);
166 GdipTransformMatrixPoints(matrix, ptf, count);
167 GdipDeleteMatrix(matrix);
168
169 for(i = 0; i < count; i++){
170 pti[i].x = roundr(ptf[i].X);
171 pti[i].y = roundr(ptf[i].Y);
172 }
173 }
174
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)
178 {
179 GpPointF ptf[4];
180 POINT pti[4];
181
182 ptf[0].X = x;
183 ptf[0].Y = y;
184 ptf[1].X = x + width;
185 ptf[1].Y = y + height;
186
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);
189
190 transform_and_round_points(graphics, pti, ptf, 4);
191
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);
194 }
195
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)
201 {
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;
213
214 if((x1 == x2) && (y1 == y2))
215 return;
216
217 theta = gdiplus_atan2(y2 - y1, x2 - x1);
218
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);
230 }
231
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;
242 }
243 else{
244 dsmall = cos(theta + M_PI_4) * size;
245 dbig = sin(theta + M_PI_4) * size;
246 }
247
248 ptf[0].X = x2 - dsmall;
249 ptf[1].X = x2 + dbig;
250
251 ptf[0].Y = y2 - dbig;
252 ptf[3].Y = y2 + dsmall;
253
254 ptf[1].Y = y2 - dsmall;
255 ptf[2].Y = y2 + dbig;
256
257 ptf[3].X = x2 - dbig;
258 ptf[2].X = x2 + dsmall;
259
260 transform_and_round_points(graphics, pt, ptf, 4);
261 Polygon(graphics->hdc, pt, 4);
262
263 break;
264 case LineCapArrowAnchor:
265 size = size * 4.0 / sqrt(3.0);
266
267 dx = cos(M_PI / 6.0 + theta) * size;
268 dy = sin(M_PI / 6.0 + theta) * size;
269
270 ptf[0].X = x2 - dx;
271 ptf[0].Y = y2 - dy;
272
273 dx = cos(- M_PI / 6.0 + theta) * size;
274 dy = sin(- M_PI / 6.0 + theta) * size;
275
276 ptf[1].X = x2 - dx;
277 ptf[1].Y = y2 - dy;
278
279 ptf[2].X = x2;
280 ptf[2].Y = y2;
281
282 transform_and_round_points(graphics, pt, ptf, 3);
283 Polygon(graphics->hdc, pt, 3);
284
285 break;
286 case LineCapRoundAnchor:
287 dx = dy = ANCHOR_WIDTH * size / 2.0;
288
289 ptf[0].X = x2 - dx;
290 ptf[0].Y = y2 - dy;
291 ptf[1].X = x2 + dx;
292 ptf[1].Y = y2 + dy;
293
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);
296
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;
302
303 ptf[0].X = x2 - dx;
304 ptf[0].Y = y2 - dy;
305 ptf[1].X = x2 + dx;
306 ptf[1].Y = y2 + dy;
307
308 dx = cos(theta) * size;
309 dy = sin(theta) * size;
310
311 ptf[2].X = x2 + dx;
312 ptf[2].Y = y2 + dy;
313
314 transform_and_round_points(graphics, pt, ptf, 3);
315 Polygon(graphics->hdc, pt, 3);
316
317 break;
318 case LineCapRound:
319 dx = dy = size / 2.0;
320
321 ptf[0].X = x2 - dx;
322 ptf[0].Y = y2 - dy;
323 ptf[1].X = x2 + dx;
324 ptf[1].Y = y2 + dy;
325
326 dx = -cos(M_PI_2 + theta) * size;
327 dy = -sin(M_PI_2 + theta) * size;
328
329 ptf[2].X = x2 - dx;
330 ptf[2].Y = y2 - dy;
331 ptf[3].X = x2 + dx;
332 ptf[3].Y = y2 + dy;
333
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);
337
338 break;
339 case LineCapCustom:
340 if(!custom)
341 break;
342
343 count = custom->pathdata.Count;
344 custptf = GdipAlloc(count * sizeof(PointF));
345 custpt = GdipAlloc(count * sizeof(POINT));
346 tp = GdipAlloc(count);
347
348 if(!custptf || !custpt || !tp || (GdipCreateMatrix(&matrix) != Ok))
349 goto custend;
350
351 memcpy(custptf, custom->pathdata.Points, count * sizeof(PointF));
352
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);
358
359 transform_and_round_points(graphics, custpt, custptf, count);
360
361 for(i = 0; i < count; i++)
362 tp[i] = convert_path_point_type(custom->pathdata.Types[i]);
363
364 if(custom->fill){
365 BeginPath(graphics->hdc);
366 PolyDraw(graphics->hdc, custpt, tp, count);
367 EndPath(graphics->hdc);
368 StrokeAndFillPath(graphics->hdc);
369 }
370 else
371 PolyDraw(graphics->hdc, custpt, tp, count);
372
373 custend:
374 GdipFree(custptf);
375 GdipFree(custpt);
376 GdipFree(tp);
377 GdipDeleteMatrix(matrix);
378 break;
379 default:
380 break;
381 }
382
383 if(!customstroke){
384 SelectObject(graphics->hdc, oldbrush);
385 SelectObject(graphics->hdc, oldpen);
386 DeleteObject(brush);
387 DeleteObject(pen);
388 }
389 }
390
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)
395 {
396 REAL dist, theta, dx, dy;
397
398 if((y1 == *y2) && (x1 == *x2))
399 return;
400
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;
405
406 *x2 = *x2 + dx;
407 *y2 = *y2 + dy;
408 }
409
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)
414 {
415 REAL dx, dy, percent;
416
417 dx = *x2 - x1;
418 dy = *y2 - y1;
419 if(dx == 0 && dy == 0)
420 return;
421
422 percent = amt / sqrt(dx * dx + dy * dy);
423 if(percent >= 1.0){
424 *x2 = x1;
425 *y2 = y1;
426 return;
427 }
428
429 shorten_line_percent(x1, y1, x2, y2, percent);
430 }
431
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)
436 {
437 POINT *pti = NULL;
438 GpPointF *ptcopy = NULL;
439 GpStatus status = GenericError;
440
441 if(!count)
442 return Ok;
443
444 pti = GdipAlloc(count * sizeof(POINT));
445 ptcopy = GdipAlloc(count * sizeof(GpPointF));
446
447 if(!pti || !ptcopy){
448 status = OutOfMemory;
449 goto end;
450 }
451
452 memcpy(ptcopy, pt, count * sizeof(GpPointF));
453
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);
462
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);
470
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);
475 }
476
477 transform_and_round_points(graphics, pti, ptcopy, count);
478
479 if(Polyline(graphics->hdc, pti, count))
480 status = Ok;
481
482 end:
483 GdipFree(pti);
484 GdipFree(ptcopy);
485
486 return status;
487 }
488
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)
497 {
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;
501
502 if(rev){
503 first = 3;
504 second = 2;
505 third = 1;
506 fourth = 0;
507 }
508
509 origx = pt[fourth].X;
510 origy = pt[fourth].Y;
511 memcpy(origpt, pt, sizeof(GpPointF) * 4);
512
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);
523
524 dx = pt[fourth].X - origx;
525 dy = pt[fourth].Y - origy;
526
527 diff = sqrt(dx * dx + dy * dy);
528 percent += 0.0005 * amt;
529 }
530 }
531
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)
536 {
537 POINT *pti;
538 GpPointF *ptcopy;
539 GpStatus status = GenericError;
540
541 if(!count)
542 return Ok;
543
544 pti = GdipAlloc(count * sizeof(POINT));
545 ptcopy = GdipAlloc(count * sizeof(GpPointF));
546
547 if(!pti || !ptcopy){
548 status = OutOfMemory;
549 goto end;
550 }
551
552 memcpy(ptcopy, pt, count * sizeof(GpPointF));
553
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);
560
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);
565
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);
573
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);
577 }
578
579 transform_and_round_points(graphics, pti, ptcopy, count);
580
581 PolyBezier(graphics->hdc, pti, count);
582
583 status = Ok;
584
585 end:
586 GdipFree(pti);
587 GdipFree(ptcopy);
588
589 return status;
590 }
591
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)
595 {
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;
601
602 if(!count){
603 status = Ok;
604 goto end;
605 }
606 if(!pti || !tp || !ptcopy){
607 status = OutOfMemory;
608 goto end;
609 }
610
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;
617 }
618 i += 2;
619 }
620 }
621
622 memcpy(ptcopy, pt, count * sizeof(GpPointF));
623
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);
634
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);
639
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);
650
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);
654
655 break;
656 default:
657 ERR("Bad path last point\n");
658 goto end;
659 }
660
661 /* Find start of points */
662 for(j = 1; j < count && ((types[j] & PathPointTypePathTypeMask)
663 == PathPointTypeStart); j++);
664
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);
672
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);
677
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);
688
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);
692
693 break;
694 default:
695 ERR("Bad path points\n");
696 goto end;
697 }
698 }
699
700 transform_and_round_points(graphics, pti, ptcopy, count);
701
702 for(i = 0; i < count; i++){
703 tp[i] = convert_path_point_type(types[i]);
704 }
705
706 PolyDraw(graphics->hdc, pti, tp, count);
707
708 status = Ok;
709
710 end:
711 GdipFree(pti);
712 GdipFree(ptcopy);
713 GdipFree(tp);
714
715 return status;
716 }
717
718 GpStatus trace_path(GpGraphics *graphics, GpPath *path)
719 {
720 GpStatus result;
721
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;
727 }
728
729 GpStatus WINGDIPAPI GdipCreateFromHDC(HDC hdc, GpGraphics **graphics)
730 {
731 TRACE("(%p, %p)\n", hdc, graphics);
732
733 return GdipCreateFromHDC2(hdc, NULL, graphics);
734 }
735
736 GpStatus WINGDIPAPI GdipCreateFromHDC2(HDC hdc, HANDLE hDevice, GpGraphics **graphics)
737 {
738 GpStatus retval;
739
740 TRACE("(%p, %p, %p)\n", hdc, hDevice, graphics);
741
742 if(hDevice != NULL) {
743 FIXME("Don't know how to hadle parameter hDevice\n");
744 return NotImplemented;
745 }
746
747 if(hdc == NULL)
748 return OutOfMemory;
749
750 if(graphics == NULL)
751 return InvalidParameter;
752
753 *graphics = GdipAlloc(sizeof(GpGraphics));
754 if(!*graphics) return OutOfMemory;
755
756 if((retval = GdipCreateMatrix(&(*graphics)->worldtrans)) != Ok){
757 GdipFree(*graphics);
758 return retval;
759 }
760
761 if((retval = GdipCreateRegion(&(*graphics)->clip)) != Ok){
762 GdipFree((*graphics)->worldtrans);
763 GdipFree(*graphics);
764 return retval;
765 }
766
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;
778
779 return Ok;
780 }
781
782 GpStatus WINGDIPAPI GdipCreateFromHWND(HWND hwnd, GpGraphics **graphics)
783 {
784 GpStatus ret;
785
786 TRACE("(%p, %p)\n", hwnd, graphics);
787
788 if((ret = GdipCreateFromHDC(GetDC(hwnd), graphics)) != Ok)
789 return ret;
790
791 (*graphics)->hwnd = hwnd;
792
793 return Ok;
794 }
795
796 /* FIXME: no icm handling */
797 GpStatus WINGDIPAPI GdipCreateFromHWNDICM(HWND hwnd, GpGraphics **graphics)
798 {
799 TRACE("(%p, %p)\n", hwnd, graphics);
800
801 return GdipCreateFromHWND(hwnd, graphics);
802 }
803
804 GpStatus WINGDIPAPI GdipCreateMetafileFromEmf(HENHMETAFILE hemf, BOOL delete,
805 GpMetafile **metafile)
806 {
807 static int calls;
808
809 if(!hemf || !metafile)
810 return InvalidParameter;
811
812 if(!(calls++))
813 FIXME("not implemented\n");
814
815 return NotImplemented;
816 }
817
818 GpStatus WINGDIPAPI GdipCreateMetafileFromWmf(HMETAFILE hwmf, BOOL delete,
819 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
820 {
821 IStream *stream = NULL;
822 UINT read;
823 BYTE* copy;
824 HENHMETAFILE hemf;
825 GpStatus retval = GenericError;
826
827 TRACE("(%p, %d, %p, %p)\n", hwmf, delete, placeable, metafile);
828
829 if(!hwmf || !metafile || !placeable)
830 return InvalidParameter;
831
832 *metafile = NULL;
833 read = GetMetaFileBitsEx(hwmf, 0, NULL);
834 if(!read)
835 return GenericError;
836 copy = GdipAlloc(read);
837 GetMetaFileBitsEx(hwmf, read, copy);
838
839 hemf = SetWinMetaFileBits(read, copy, NULL, NULL);
840 GdipFree(copy);
841
842 read = GetEnhMetaFileBits(hemf, 0, NULL);
843 copy = GdipAlloc(read);
844 GetEnhMetaFileBits(hemf, read, copy);
845 DeleteEnhMetaFile(hemf);
846
847 if(CreateStreamOnHGlobal(copy, TRUE, &stream) != S_OK){
848 ERR("could not make stream\n");
849 GdipFree(copy);
850 goto err;
851 }
852
853 *metafile = GdipAlloc(sizeof(GpMetafile));
854 if(!*metafile){
855 retval = OutOfMemory;
856 goto err;
857 }
858
859 if(OleLoadPicture(stream, 0, FALSE, &IID_IPicture,
860 (LPVOID*) &((*metafile)->image.picture)) != S_OK)
861 goto err;
862
863
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;
872
873 if(delete)
874 DeleteMetaFile(hwmf);
875
876 return Ok;
877
878 err:
879 GdipFree(*metafile);
880 IStream_Release(stream);
881 return retval;
882 }
883
884 GpStatus WINGDIPAPI GdipCreateMetafileFromWmfFile(GDIPCONST WCHAR *file,
885 GDIPCONST WmfPlaceableFileHeader * placeable, GpMetafile **metafile)
886 {
887 HMETAFILE hmf = GetMetaFileW(file);
888
889 TRACE("(%s, %p, %p)\n", debugstr_w(file), placeable, metafile);
890
891 if(!hmf) return InvalidParameter;
892
893 return GdipCreateMetafileFromWmf(hmf, TRUE, placeable, metafile);
894 }
895
896 GpStatus WINGDIPAPI GdipCreateStreamOnFile(GDIPCONST WCHAR * filename,
897 UINT access, IStream **stream)
898 {
899 DWORD dwMode;
900 HRESULT ret;
901
902 TRACE("(%s, %u, %p)\n", debugstr_w(filename), access, stream);
903
904 if(!stream || !filename)
905 return InvalidParameter;
906
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;
913
914 ret = SHCreateStreamOnFileW(filename, dwMode, stream);
915
916 return hresult_to_status(ret);
917 }
918
919 GpStatus WINGDIPAPI GdipDeleteGraphics(GpGraphics *graphics)
920 {
921 TRACE("(%p)\n", graphics);
922
923 if(!graphics) return InvalidParameter;
924 if(graphics->busy) return ObjectBusy;
925
926 if(graphics->hwnd)
927 ReleaseDC(graphics->hwnd, graphics->hdc);
928
929 GdipDeleteRegion(graphics->clip);
930 GdipDeleteMatrix(graphics->worldtrans);
931 GdipFree(graphics);
932
933 return Ok;
934 }
935
936 GpStatus WINGDIPAPI GdipDrawArc(GpGraphics *graphics, GpPen *pen, REAL x,
937 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
938 {
939 INT save_state, num_pts;
940 GpPointF points[MAX_ARC_PTS];
941 GpStatus retval;
942
943 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
944 width, height, startAngle, sweepAngle);
945
946 if(!graphics || !pen || width <= 0 || height <= 0)
947 return InvalidParameter;
948
949 if(graphics->busy)
950 return ObjectBusy;
951
952 num_pts = arc2polybezier(points, x, y, width, height, startAngle, sweepAngle);
953
954 save_state = prepare_dc(graphics, pen);
955
956 retval = draw_polybezier(graphics, pen, points, num_pts, TRUE);
957
958 restore_dc(graphics, save_state);
959
960 return retval;
961 }
962
963 GpStatus WINGDIPAPI GdipDrawArcI(GpGraphics *graphics, GpPen *pen, INT x,
964 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
965 {
966 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
967 width, height, startAngle, sweepAngle);
968
969 return GdipDrawArc(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
970 }
971
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)
974 {
975 INT save_state;
976 GpPointF pt[4];
977 GpStatus retval;
978
979 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1,
980 x2, y2, x3, y3, x4, y4);
981
982 if(!graphics || !pen)
983 return InvalidParameter;
984
985 if(graphics->busy)
986 return ObjectBusy;
987
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;
996
997 save_state = prepare_dc(graphics, pen);
998
999 retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1000
1001 restore_dc(graphics, save_state);
1002
1003 return retval;
1004 }
1005
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)
1008 {
1009 INT save_state;
1010 GpPointF pt[4];
1011 GpStatus retval;
1012
1013 TRACE("(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d)\n", graphics, pen, x1, y1,
1014 x2, y2, x3, y3, x4, y4);
1015
1016 if(!graphics || !pen)
1017 return InvalidParameter;
1018
1019 if(graphics->busy)
1020 return ObjectBusy;
1021
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;
1030
1031 save_state = prepare_dc(graphics, pen);
1032
1033 retval = draw_polybezier(graphics, pen, pt, 4, TRUE);
1034
1035 restore_dc(graphics, save_state);
1036
1037 return retval;
1038 }
1039
1040 GpStatus WINGDIPAPI GdipDrawBeziers(GpGraphics *graphics, GpPen *pen,
1041 GDIPCONST GpPointF *points, INT count)
1042 {
1043 INT i;
1044 GpStatus ret;
1045
1046 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1047
1048 if(!graphics || !pen || !points || (count <= 0))
1049 return InvalidParameter;
1050
1051 if(graphics->busy)
1052 return ObjectBusy;
1053
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;
1062 }
1063
1064 return Ok;
1065 }
1066
1067 GpStatus WINGDIPAPI GdipDrawBeziersI(GpGraphics *graphics, GpPen *pen,
1068 GDIPCONST GpPoint *points, INT count)
1069 {
1070 GpPointF *pts;
1071 GpStatus ret;
1072 INT i;
1073
1074 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1075
1076 if(!graphics || !pen || !points || (count <= 0))
1077 return InvalidParameter;
1078
1079 if(graphics->busy)
1080 return ObjectBusy;
1081
1082 pts = GdipAlloc(sizeof(GpPointF) * count);
1083 if(!pts)
1084 return OutOfMemory;
1085
1086 for(i = 0; i < count; i++){
1087 pts[i].X = (REAL)points[i].X;
1088 pts[i].Y = (REAL)points[i].Y;
1089 }
1090
1091 ret = GdipDrawBeziers(graphics,pen,pts,count);
1092
1093 GdipFree(pts);
1094
1095 return ret;
1096 }
1097
1098 GpStatus WINGDIPAPI GdipDrawClosedCurve(GpGraphics *graphics, GpPen *pen,
1099 GDIPCONST GpPointF *points, INT count)
1100 {
1101 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1102
1103 return GdipDrawClosedCurve2(graphics, pen, points, count, 1.0);
1104 }
1105
1106 GpStatus WINGDIPAPI GdipDrawClosedCurveI(GpGraphics *graphics, GpPen *pen,
1107 GDIPCONST GpPoint *points, INT count)
1108 {
1109 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1110
1111 return GdipDrawClosedCurve2I(graphics, pen, points, count, 1.0);
1112 }
1113
1114 GpStatus WINGDIPAPI GdipDrawClosedCurve2(GpGraphics *graphics, GpPen *pen,
1115 GDIPCONST GpPointF *points, INT count, REAL tension)
1116 {
1117 GpPath *path;
1118 GpStatus stat;
1119
1120 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1121
1122 if(!graphics || !pen || !points || count <= 0)
1123 return InvalidParameter;
1124
1125 if(graphics->busy)
1126 return ObjectBusy;
1127
1128 if((stat = GdipCreatePath(FillModeAlternate, &path)) != Ok)
1129 return stat;
1130
1131 stat = GdipAddPathClosedCurve2(path, points, count, tension);
1132 if(stat != Ok){
1133 GdipDeletePath(path);
1134 return stat;
1135 }
1136
1137 stat = GdipDrawPath(graphics, pen, path);
1138
1139 GdipDeletePath(path);
1140
1141 return stat;
1142 }
1143
1144 GpStatus WINGDIPAPI GdipDrawClosedCurve2I(GpGraphics *graphics, GpPen *pen,
1145 GDIPCONST GpPoint *points, INT count, REAL tension)
1146 {
1147 GpPointF *ptf;
1148 GpStatus stat;
1149 INT i;
1150
1151 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1152
1153 if(!points || count <= 0)
1154 return InvalidParameter;
1155
1156 ptf = GdipAlloc(sizeof(GpPointF)*count);
1157 if(!ptf)
1158 return OutOfMemory;
1159
1160 for(i = 0; i < count; i++){
1161 ptf[i].X = (REAL)points[i].X;
1162 ptf[i].Y = (REAL)points[i].Y;
1163 }
1164
1165 stat = GdipDrawClosedCurve2(graphics, pen, ptf, count, tension);
1166
1167 GdipFree(ptf);
1168
1169 return stat;
1170 }
1171
1172 GpStatus WINGDIPAPI GdipDrawCurve(GpGraphics *graphics, GpPen *pen,
1173 GDIPCONST GpPointF *points, INT count)
1174 {
1175 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1176
1177 return GdipDrawCurve2(graphics,pen,points,count,1.0);
1178 }
1179
1180 GpStatus WINGDIPAPI GdipDrawCurveI(GpGraphics *graphics, GpPen *pen,
1181 GDIPCONST GpPoint *points, INT count)
1182 {
1183 GpPointF *pointsF;
1184 GpStatus ret;
1185 INT i;
1186
1187 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1188
1189 if(!points || count <= 0)
1190 return InvalidParameter;
1191
1192 pointsF = GdipAlloc(sizeof(GpPointF)*count);
1193 if(!pointsF)
1194 return OutOfMemory;
1195
1196 for(i = 0; i < count; i++){
1197 pointsF[i].X = (REAL)points[i].X;
1198 pointsF[i].Y = (REAL)points[i].Y;
1199 }
1200
1201 ret = GdipDrawCurve(graphics,pen,pointsF,count);
1202 GdipFree(pointsF);
1203
1204 return ret;
1205 }
1206
1207 /* Approximates cardinal spline with Bezier curves. */
1208 GpStatus WINGDIPAPI GdipDrawCurve2(GpGraphics *graphics, GpPen *pen,
1209 GDIPCONST GpPointF *points, INT count, REAL tension)
1210 {
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;
1216
1217 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1218
1219 if(!graphics || !pen)
1220 return InvalidParameter;
1221
1222 if(graphics->busy)
1223 return ObjectBusy;
1224
1225 pt = GdipAlloc(len_pt * sizeof(GpPointF));
1226 tension = tension * TENSION_CONST;
1227
1228 calc_curve_bezier_endp(points[0].X, points[0].Y, points[1].X, points[1].Y,
1229 tension, &x1, &y1);
1230
1231 pt[0].X = points[0].X;
1232 pt[0].Y = points[0].Y;
1233 pt[1].X = x1;
1234 pt[1].Y = y1;
1235
1236 for(i = 0; i < count-2; i++){
1237 calc_curve_bezier(&(points[i]), tension, &x1, &y1, &x2, &y2);
1238
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;
1245 }
1246
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);
1249
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;
1254
1255 save_state = prepare_dc(graphics, pen);
1256
1257 retval = draw_polybezier(graphics, pen, pt, len_pt, TRUE);
1258
1259 GdipFree(pt);
1260 restore_dc(graphics, save_state);
1261
1262 return retval;
1263 }
1264
1265 GpStatus WINGDIPAPI GdipDrawCurve2I(GpGraphics *graphics, GpPen *pen,
1266 GDIPCONST GpPoint *points, INT count, REAL tension)
1267 {
1268 GpPointF *pointsF;
1269 GpStatus ret;
1270 INT i;
1271
1272 TRACE("(%p, %p, %p, %d, %.2f)\n", graphics, pen, points, count, tension);
1273
1274 if(!points || count <= 0)
1275 return InvalidParameter;
1276
1277 pointsF = GdipAlloc(sizeof(GpPointF)*count);
1278 if(!pointsF)
1279 return OutOfMemory;
1280
1281 for(i = 0; i < count; i++){
1282 pointsF[i].X = (REAL)points[i].X;
1283 pointsF[i].Y = (REAL)points[i].Y;
1284 }
1285
1286 ret = GdipDrawCurve2(graphics,pen,pointsF,count,tension);
1287 GdipFree(pointsF);
1288
1289 return ret;
1290 }
1291
1292 GpStatus WINGDIPAPI GdipDrawEllipse(GpGraphics *graphics, GpPen *pen, REAL x,
1293 REAL y, REAL width, REAL height)
1294 {
1295 INT save_state;
1296 GpPointF ptf[2];
1297 POINT pti[2];
1298
1299 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1300
1301 if(!graphics || !pen)
1302 return InvalidParameter;
1303
1304 if(graphics->busy)
1305 return ObjectBusy;
1306
1307 ptf[0].X = x;
1308 ptf[0].Y = y;
1309 ptf[1].X = x + width;
1310 ptf[1].Y = y + height;
1311
1312 save_state = prepare_dc(graphics, pen);
1313 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1314
1315 transform_and_round_points(graphics, pti, ptf, 2);
1316
1317 Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
1318
1319 restore_dc(graphics, save_state);
1320
1321 return Ok;
1322 }
1323
1324 GpStatus WINGDIPAPI GdipDrawEllipseI(GpGraphics *graphics, GpPen *pen, INT x,
1325 INT y, INT width, INT height)
1326 {
1327 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1328
1329 return GdipDrawEllipse(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1330 }
1331
1332
1333 GpStatus WINGDIPAPI GdipDrawImage(GpGraphics *graphics, GpImage *image, REAL x, REAL y)
1334 {
1335 TRACE("(%p, %p, %.2f, %.2f)\n", graphics, image, x, y);
1336
1337 /* IPicture::Render uses LONG coords */
1338 return GdipDrawImageI(graphics,image,roundr(x),roundr(y));
1339 }
1340
1341 GpStatus WINGDIPAPI GdipDrawImageI(GpGraphics *graphics, GpImage *image, INT x,
1342 INT y)
1343 {
1344 UINT width, height, srcw, srch;
1345
1346 TRACE("(%p, %p, %d, %d)\n", graphics, image, x, y);
1347
1348 if(!graphics || !image)
1349 return InvalidParameter;
1350
1351 GdipGetImageWidth(image, &width);
1352 GdipGetImageHeight(image, &height);
1353
1354 srcw = width * (((REAL) INCH_HIMETRIC) /
1355 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSX)));
1356 srch = height * (((REAL) INCH_HIMETRIC) /
1357 ((REAL) GetDeviceCaps(graphics->hdc, LOGPIXELSY)));
1358
1359 if(image->type != ImageTypeMetafile){
1360 y += height;
1361 height *= -1;
1362 }
1363
1364 IPicture_Render(image->picture, graphics->hdc, x, y, width, height,
1365 0, 0, srcw, srch, NULL);
1366
1367 return Ok;
1368 }
1369
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)
1375 {
1376 GpPointF ptf[3];
1377 POINT pti[3];
1378 REAL dx, dy;
1379
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);
1383
1384 if(!graphics || !image || !points || count != 3)
1385 return InvalidParameter;
1386
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));
1394 }
1395 else
1396 return NotImplemented;
1397
1398 memcpy(ptf, points, 3 * sizeof(GpPointF));
1399 transform_and_round_points(graphics, pti, ptf, 3);
1400
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;
1408 }
1409
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;
1418 }
1419
1420 return Ok;
1421 }
1422
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)
1427 {
1428 GpPointF pointsF[3];
1429 INT i;
1430
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);
1434
1435 if(!points || count!=3)
1436 return InvalidParameter;
1437
1438 for(i = 0; i < count; i++){
1439 pointsF[i].X = (REAL)points[i].X;
1440 pointsF[i].Y = (REAL)points[i].Y;
1441 }
1442
1443 return GdipDrawImagePointsRect(graphics, image, pointsF, count, (REAL)srcx, (REAL)srcy,
1444 (REAL)srcwidth, (REAL)srcheight, srcUnit, imageAttributes,
1445 callback, callbackData);
1446 }
1447
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)
1453 {
1454 GpPointF points[3];
1455
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);
1459
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;
1466
1467 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1468 srcwidth, srcheight, srcUnit, imageattr, callback, callbackData);
1469 }
1470
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)
1476 {
1477 GpPointF points[3];
1478
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);
1482
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;
1489
1490 return GdipDrawImagePointsRect(graphics, image, points, 3, srcx, srcy,
1491 srcwidth, srcheight, srcUnit, imageAttributes, callback, callbackData);
1492 }
1493
1494 GpStatus WINGDIPAPI GdipDrawImageRect(GpGraphics *graphics, GpImage *image,
1495 REAL x, REAL y, REAL width, REAL height)
1496 {
1497 RectF bounds;
1498 GpUnit unit;
1499 GpStatus ret;
1500
1501 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, image, x, y, width, height);
1502
1503 if(!graphics || !image)
1504 return InvalidParameter;
1505
1506 ret = GdipGetImageBounds(image, &bounds, &unit);
1507 if(ret != Ok)
1508 return ret;
1509
1510 return GdipDrawImageRectRect(graphics, image, x, y, width, height,
1511 bounds.X, bounds.Y, bounds.Width, bounds.Height,
1512 unit, NULL, NULL, NULL);
1513 }
1514
1515 GpStatus WINGDIPAPI GdipDrawImageRectI(GpGraphics *graphics, GpImage *image,
1516 INT x, INT y, INT width, INT height)
1517 {
1518 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, image, x, y, width, height);
1519
1520 return GdipDrawImageRect(graphics, image, (REAL)x, (REAL)y, (REAL)width, (REAL)height);
1521 }
1522
1523 GpStatus WINGDIPAPI GdipDrawLine(GpGraphics *graphics, GpPen *pen, REAL x1,
1524 REAL y1, REAL x2, REAL y2)
1525 {
1526 INT save_state;
1527 GpPointF pt[2];
1528 GpStatus retval;
1529
1530 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x1, y1, x2, y2);
1531
1532 if(!pen || !graphics)
1533 return InvalidParameter;
1534
1535 if(graphics->busy)
1536 return ObjectBusy;
1537
1538 pt[0].X = x1;
1539 pt[0].Y = y1;
1540 pt[1].X = x2;
1541 pt[1].Y = y2;
1542
1543 save_state = prepare_dc(graphics, pen);
1544
1545 retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1546
1547 restore_dc(graphics, save_state);
1548
1549 return retval;
1550 }
1551
1552 GpStatus WINGDIPAPI GdipDrawLineI(GpGraphics *graphics, GpPen *pen, INT x1,
1553 INT y1, INT x2, INT y2)
1554 {
1555 INT save_state;
1556 GpPointF pt[2];
1557 GpStatus retval;
1558
1559 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x1, y1, x2, y2);
1560
1561 if(!pen || !graphics)
1562 return InvalidParameter;
1563
1564 if(graphics->busy)
1565 return ObjectBusy;
1566
1567 pt[0].X = (REAL)x1;
1568 pt[0].Y = (REAL)y1;
1569 pt[1].X = (REAL)x2;
1570 pt[1].Y = (REAL)y2;
1571
1572 save_state = prepare_dc(graphics, pen);
1573
1574 retval = draw_polyline(graphics, pen, pt, 2, TRUE);
1575
1576 restore_dc(graphics, save_state);
1577
1578 return retval;
1579 }
1580
1581 GpStatus WINGDIPAPI GdipDrawLines(GpGraphics *graphics, GpPen *pen, GDIPCONST
1582 GpPointF *points, INT count)
1583 {
1584 INT save_state;
1585 GpStatus retval;
1586
1587 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1588
1589 if(!pen || !graphics || (count < 2))
1590 return InvalidParameter;
1591
1592 if(graphics->busy)
1593 return ObjectBusy;
1594
1595 save_state = prepare_dc(graphics, pen);
1596
1597 retval = draw_polyline(graphics, pen, points, count, TRUE);
1598
1599 restore_dc(graphics, save_state);
1600
1601 return retval;
1602 }
1603
1604 GpStatus WINGDIPAPI GdipDrawLinesI(GpGraphics *graphics, GpPen *pen, GDIPCONST
1605 GpPoint *points, INT count)
1606 {
1607 INT save_state;
1608 GpStatus retval;
1609 GpPointF *ptf = NULL;
1610 int i;
1611
1612 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
1613
1614 if(!pen || !graphics || (count < 2))
1615 return InvalidParameter;
1616
1617 if(graphics->busy)
1618 return ObjectBusy;
1619
1620 ptf = GdipAlloc(count * sizeof(GpPointF));
1621 if(!ptf) return OutOfMemory;
1622
1623 for(i = 0; i < count; i ++){
1624 ptf[i].X = (REAL) points[i].X;
1625 ptf[i].Y = (REAL) points[i].Y;
1626 }
1627
1628 save_state = prepare_dc(graphics, pen);
1629
1630 retval = draw_polyline(graphics, pen, ptf, count, TRUE);
1631
1632 restore_dc(graphics, save_state);
1633
1634 GdipFree(ptf);
1635 return retval;
1636 }
1637
1638 GpStatus WINGDIPAPI GdipDrawPath(GpGraphics *graphics, GpPen *pen, GpPath *path)
1639 {
1640 INT save_state;
1641 GpStatus retval;
1642
1643 TRACE("(%p, %p, %p)\n", graphics, pen, path);
1644
1645 if(!pen || !graphics)
1646 return InvalidParameter;
1647
1648 if(graphics->busy)
1649 return ObjectBusy;
1650
1651 save_state = prepare_dc(graphics, pen);
1652
1653 retval = draw_poly(graphics, pen, path->pathdata.Points,
1654 path->pathdata.Types, path->pathdata.Count, TRUE);
1655
1656 restore_dc(graphics, save_state);
1657
1658 return retval;
1659 }
1660
1661 GpStatus WINGDIPAPI GdipDrawPie(GpGraphics *graphics, GpPen *pen, REAL x,
1662 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
1663 {
1664 INT save_state;
1665
1666 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y,
1667 width, height, startAngle, sweepAngle);
1668
1669 if(!graphics || !pen)
1670 return InvalidParameter;
1671
1672 if(graphics->busy)
1673 return ObjectBusy;
1674
1675 save_state = prepare_dc(graphics, pen);
1676 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1677
1678 draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
1679
1680 restore_dc(graphics, save_state);
1681
1682 return Ok;
1683 }
1684
1685 GpStatus WINGDIPAPI GdipDrawPieI(GpGraphics *graphics, GpPen *pen, INT x,
1686 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
1687 {
1688 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n", graphics, pen, x, y,
1689 width, height, startAngle, sweepAngle);
1690
1691 return GdipDrawPie(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
1692 }
1693
1694 GpStatus WINGDIPAPI GdipDrawRectangle(GpGraphics *graphics, GpPen *pen, REAL x,
1695 REAL y, REAL width, REAL height)
1696 {
1697 INT save_state;
1698 GpPointF ptf[4];
1699 POINT pti[4];
1700
1701 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, pen, x, y, width, height);
1702
1703 if(!pen || !graphics)
1704 return InvalidParameter;
1705
1706 if(graphics->busy)
1707 return ObjectBusy;
1708
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;
1717
1718 save_state = prepare_dc(graphics, pen);
1719 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1720
1721 transform_and_round_points(graphics, pti, ptf, 4);
1722 Polygon(graphics->hdc, pti, 4);
1723
1724 restore_dc(graphics, save_state);
1725
1726 return Ok;
1727 }
1728
1729 GpStatus WINGDIPAPI GdipDrawRectangleI(GpGraphics *graphics, GpPen *pen, INT x,
1730 INT y, INT width, INT height)
1731 {
1732 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, pen, x, y, width, height);
1733
1734 return GdipDrawRectangle(graphics,pen,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
1735 }
1736
1737 GpStatus WINGDIPAPI GdipDrawRectangles(GpGraphics *graphics, GpPen *pen,
1738 GDIPCONST GpRectF* rects, INT count)
1739 {
1740 GpPointF *ptf;
1741 POINT *pti;
1742 INT save_state, i;
1743
1744 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1745
1746 if(!graphics || !pen || !rects || count < 1)
1747 return InvalidParameter;
1748
1749 if(graphics->busy)
1750 return ObjectBusy;
1751
1752 ptf = GdipAlloc(4 * count * sizeof(GpPointF));
1753 pti = GdipAlloc(4 * count * sizeof(POINT));
1754
1755 if(!ptf || !pti){
1756 GdipFree(ptf);
1757 GdipFree(pti);
1758 return OutOfMemory;
1759 }
1760
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;
1766 }
1767
1768 save_state = prepare_dc(graphics, pen);
1769 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
1770
1771 transform_and_round_points(graphics, pti, ptf, 4 * count);
1772
1773 for(i = 0; i < count; i++)
1774 Polygon(graphics->hdc, &pti[4 * i], 4);
1775
1776 restore_dc(graphics, save_state);
1777
1778 GdipFree(ptf);
1779 GdipFree(pti);
1780
1781 return Ok;
1782 }
1783
1784 GpStatus WINGDIPAPI GdipDrawRectanglesI(GpGraphics *graphics, GpPen *pen,
1785 GDIPCONST GpRect* rects, INT count)
1786 {
1787 GpRectF *rectsF;
1788 GpStatus ret;
1789 INT i;
1790
1791 TRACE("(%p, %p, %p, %d)\n", graphics, pen, rects, count);
1792
1793 if(!rects || count<=0)
1794 return InvalidParameter;
1795
1796 rectsF = GdipAlloc(sizeof(GpRectF) * count);
1797 if(!rectsF)
1798 return OutOfMemory;
1799
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;
1805 }
1806
1807 ret = GdipDrawRectangles(graphics, pen, rectsF, count);
1808 GdipFree(rectsF);
1809
1810 return ret;
1811 }
1812
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)
1816 {
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;
1829
1830 if(!graphics || !string || !font || !brush || !rect)
1831 return InvalidParameter;
1832
1833 if((brush->bt != BrushTypeSolidColor)){
1834 FIXME("not implemented for given parameters\n");
1835 return NotImplemented;
1836 }
1837
1838 if(format)
1839 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
1840
1841 if(length == -1) length = lstrlenW(string);
1842
1843 stringdup = GdipAlloc(length * sizeof(WCHAR));
1844 if(!stringdup) return OutOfMemory;
1845
1846 save_state = SaveDC(graphics->hdc);
1847 SetBkMode(graphics->hdc, TRANSPARENT);
1848 SetTextColor(graphics->hdc, brush->lb.lbColor);
1849
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);
1855
1856 if(roundr(rect->Width) == 0 && roundr(rect->Height) == 0){
1857 rel_width = rel_height = 1.0;
1858 nwidth = nheight = INT_MAX;
1859 }
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;
1867
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);
1872 }
1873
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;
1879
1880 lfw.lfHeight = roundr(((REAL)lfw.lfHeight) * rel_height);
1881 lfw.lfWidth = roundr(textmet.tmAveCharWidth * rel_width);
1882
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);
1892
1893 gdifont = CreateFontIndirectW(&lfw);
1894 DeleteObject(SelectObject(graphics->hdc, CreateFontIndirectW(&lfw)));
1895
1896 for(i = 0, j = 0; i < length; i++){
1897 if(!isprint(string[i]) && (string[i] != '\n'))
1898 continue;
1899
1900 stringdup[j] = string[i];
1901 j++;
1902 }
1903
1904 stringdup[j] = 0;
1905 length = j;
1906
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);
1910
1911 GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
1912 nwidth, &fit, NULL, &size);
1913 fitcpy = fit;
1914
1915 if(fit == 0){
1916 DrawTextW(graphics->hdc, stringdup + sum, 1, &drawcoord, DT_NOCLIP |
1917 DT_EXPANDTABS);
1918 break;
1919 }
1920
1921 for(lret = 0; lret < fit; lret++)
1922 if(*(stringdup + sum + lret) == '\n')
1923 break;
1924
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--;
1935
1936 if(*(stringdup + sum + fit) == '\t')
1937 break;
1938
1939 if(fit == 0){
1940 fit = fitcpy;
1941 break;
1942 }
1943 }
1944 }
1945 DrawTextW(graphics->hdc, stringdup + sum, min(length - sum, fit),
1946 &drawcoord, DT_NOCLIP | DT_EXPANDTABS);
1947
1948 sum += fit + (lret < fitcpy ? 1 : 0);
1949 height += size.cy;
1950
1951 if(height > nheight)
1952 break;
1953
1954 /* Stop if this was a linewrap (but not if it was a linebreak). */
1955 if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
1956 break;
1957 }
1958
1959 GdipFree(stringdup);
1960 DeleteObject(rgn);
1961 DeleteObject(gdifont);
1962
1963 RestoreDC(graphics->hdc, save_state);
1964
1965 return Ok;
1966 }
1967
1968 GpStatus WINGDIPAPI GdipFillClosedCurve2(GpGraphics *graphics, GpBrush *brush,
1969 GDIPCONST GpPointF *points, INT count, REAL tension, GpFillMode fill)
1970 {
1971 GpPath *path;
1972 GpStatus stat;
1973
1974 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
1975 count, tension, fill);
1976
1977 if(!graphics || !brush || !points)
1978 return InvalidParameter;
1979
1980 if(graphics->busy)
1981 return ObjectBusy;
1982
1983 stat = GdipCreatePath(fill, &path);
1984 if(stat != Ok)
1985 return stat;
1986
1987 stat = GdipAddPathClosedCurve2(path, points, count, tension);
1988 if(stat != Ok){
1989 GdipDeletePath(path);
1990 return stat;
1991 }
1992
1993 stat = GdipFillPath(graphics, brush, path);
1994 if(stat != Ok){
1995 GdipDeletePath(path);
1996 return stat;
1997 }
1998
1999 GdipDeletePath(path);
2000
2001 return Ok;
2002 }
2003
2004 GpStatus WINGDIPAPI GdipFillClosedCurve2I(GpGraphics *graphics, GpBrush *brush,
2005 GDIPCONST GpPoint *points, INT count, REAL tension, GpFillMode fill)
2006 {
2007 GpPointF *ptf;
2008 GpStatus stat;
2009 INT i;
2010
2011 TRACE("(%p, %p, %p, %d, %.2f, %d)\n", graphics, brush, points,
2012 count, tension, fill);
2013
2014 if(!points || count <= 0)
2015 return InvalidParameter;
2016
2017 ptf = GdipAlloc(sizeof(GpPointF)*count);
2018 if(!ptf)
2019 return OutOfMemory;
2020
2021 for(i = 0;i < count;i++){
2022 ptf[i].X = (REAL)points[i].X;
2023 ptf[i].Y = (REAL)points[i].Y;
2024 }
2025
2026 stat = GdipFillClosedCurve2(graphics, brush, ptf, count, tension, fill);
2027
2028 GdipFree(ptf);
2029
2030 return stat;
2031 }
2032
2033 GpStatus WINGDIPAPI GdipFillEllipse(GpGraphics *graphics, GpBrush *brush, REAL x,
2034 REAL y, REAL width, REAL height)
2035 {
2036 INT save_state;
2037 GpPointF ptf[2];
2038 POINT pti[2];
2039
2040 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2041
2042 if(!graphics || !brush)
2043 return InvalidParameter;
2044
2045 if(graphics->busy)
2046 return ObjectBusy;
2047
2048 ptf[0].X = x;
2049 ptf[0].Y = y;
2050 ptf[1].X = x + width;
2051 ptf[1].Y = y + height;
2052
2053 save_state = SaveDC(graphics->hdc);
2054 EndPath(graphics->hdc);
2055 SelectObject(graphics->hdc, brush->gdibrush);
2056 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2057
2058 transform_and_round_points(graphics, pti, ptf, 2);
2059
2060 Ellipse(graphics->hdc, pti[0].x, pti[0].y, pti[1].x, pti[1].y);
2061
2062 RestoreDC(graphics->hdc, save_state);
2063
2064 return Ok;
2065 }
2066
2067 GpStatus WINGDIPAPI GdipFillEllipseI(GpGraphics *graphics, GpBrush *brush, INT x,
2068 INT y, INT width, INT height)
2069 {
2070 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2071
2072 return GdipFillEllipse(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height);
2073 }
2074
2075 GpStatus WINGDIPAPI GdipFillPath(GpGraphics *graphics, GpBrush *brush, GpPath *path)
2076 {
2077 INT save_state;
2078 GpStatus retval;
2079
2080 TRACE("(%p, %p, %p)\n", graphics, brush, path);
2081
2082 if(!brush || !graphics || !path)
2083 return InvalidParameter;
2084
2085 if(graphics->busy)
2086 return ObjectBusy;
2087
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));
2093
2094 BeginPath(graphics->hdc);
2095 retval = draw_poly(graphics, NULL, path->pathdata.Points,
2096 path->pathdata.Types, path->pathdata.Count, FALSE);
2097
2098 if(retval != Ok)
2099 goto end;
2100
2101 EndPath(graphics->hdc);
2102 FillPath(graphics->hdc);
2103
2104 retval = Ok;
2105
2106 end:
2107 RestoreDC(graphics->hdc, save_state);
2108
2109 return retval;
2110 }
2111
2112 GpStatus WINGDIPAPI GdipFillPie(GpGraphics *graphics, GpBrush *brush, REAL x,
2113 REAL y, REAL width, REAL height, REAL startAngle, REAL sweepAngle)
2114 {
2115 INT save_state;
2116
2117 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f)\n",
2118 graphics, brush, x, y, width, height, startAngle, sweepAngle);
2119
2120 if(!graphics || !brush)
2121 return InvalidParameter;
2122
2123 if(graphics->busy)
2124 return ObjectBusy;
2125
2126 save_state = SaveDC(graphics->hdc);
2127 EndPath(graphics->hdc);
2128 SelectObject(graphics->hdc, brush->gdibrush);
2129 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2130
2131 draw_pie(graphics, x, y, width, height, startAngle, sweepAngle);
2132
2133 RestoreDC(graphics->hdc, save_state);
2134
2135 return Ok;
2136 }
2137
2138 GpStatus WINGDIPAPI GdipFillPieI(GpGraphics *graphics, GpBrush *brush, INT x,
2139 INT y, INT width, INT height, REAL startAngle, REAL sweepAngle)
2140 {
2141 TRACE("(%p, %p, %d, %d, %d, %d, %.2f, %.2f)\n",
2142 graphics, brush, x, y, width, height, startAngle, sweepAngle);
2143
2144 return GdipFillPie(graphics,brush,(REAL)x,(REAL)y,(REAL)width,(REAL)height,startAngle,sweepAngle);
2145 }
2146
2147 GpStatus WINGDIPAPI GdipFillPolygon(GpGraphics *graphics, GpBrush *brush,
2148 GDIPCONST GpPointF *points, INT count, GpFillMode fillMode)
2149 {
2150 INT save_state;
2151 GpPointF *ptf = NULL;
2152 POINT *pti = NULL;
2153 GpStatus retval = Ok;
2154
2155 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2156
2157 if(!graphics || !brush || !points || !count)
2158 return InvalidParameter;
2159
2160 if(graphics->busy)
2161 return ObjectBusy;
2162
2163 ptf = GdipAlloc(count * sizeof(GpPointF));
2164 pti = GdipAlloc(count * sizeof(POINT));
2165 if(!ptf || !pti){
2166 retval = OutOfMemory;
2167 goto end;
2168 }
2169
2170 memcpy(ptf, points, count * sizeof(GpPointF));
2171
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));
2178
2179 transform_and_round_points(graphics, pti, ptf, count);
2180 Polygon(graphics->hdc, pti, count);
2181
2182 RestoreDC(graphics->hdc, save_state);
2183
2184 end:
2185 GdipFree(ptf);
2186 GdipFree(pti);
2187
2188 return retval;
2189 }
2190
2191 GpStatus WINGDIPAPI GdipFillPolygonI(GpGraphics *graphics, GpBrush *brush,
2192 GDIPCONST GpPoint *points, INT count, GpFillMode fillMode)
2193 {
2194 INT save_state, i;
2195 GpPointF *ptf = NULL;
2196 POINT *pti = NULL;
2197 GpStatus retval = Ok;
2198
2199 TRACE("(%p, %p, %p, %d, %d)\n", graphics, brush, points, count, fillMode);
2200
2201 if(!graphics || !brush || !points || !count)
2202 return InvalidParameter;
2203
2204 if(graphics->busy)
2205 return ObjectBusy;
2206
2207 ptf = GdipAlloc(count * sizeof(GpPointF));
2208 pti = GdipAlloc(count * sizeof(POINT));
2209 if(!ptf || !pti){
2210 retval = OutOfMemory;
2211 goto end;
2212 }
2213
2214 for(i = 0; i < count; i ++){
2215 ptf[i].X = (REAL) points[i].X;
2216 ptf[i].Y = (REAL) points[i].Y;
2217 }
2218
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));
2225
2226 transform_and_round_points(graphics, pti, ptf, count);
2227 Polygon(graphics->hdc, pti, count);
2228
2229 RestoreDC(graphics->hdc, save_state);
2230
2231 end:
2232 GdipFree(ptf);
2233 GdipFree(pti);
2234
2235 return retval;
2236 }
2237
2238 GpStatus WINGDIPAPI GdipFillPolygon2(GpGraphics *graphics, GpBrush *brush,
2239 GDIPCONST GpPointF *points, INT count)
2240 {
2241 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2242
2243 return GdipFillPolygon(graphics, brush, points, count, FillModeAlternate);
2244 }
2245
2246 GpStatus WINGDIPAPI GdipFillPolygon2I(GpGraphics *graphics, GpBrush *brush,
2247 GDIPCONST GpPoint *points, INT count)
2248 {
2249 TRACE("(%p, %p, %p, %d)\n", graphics, brush, points, count);
2250
2251 return GdipFillPolygonI(graphics, brush, points, count, FillModeAlternate);
2252 }
2253
2254 GpStatus WINGDIPAPI GdipFillRectangle(GpGraphics *graphics, GpBrush *brush,
2255 REAL x, REAL y, REAL width, REAL height)
2256 {
2257 INT save_state;
2258 GpPointF ptf[4];
2259 POINT pti[4];
2260
2261 TRACE("(%p, %p, %.2f, %.2f, %.2f, %.2f)\n", graphics, brush, x, y, width, height);
2262
2263 if(!graphics || !brush)
2264 return InvalidParameter;
2265
2266 if(graphics->busy)
2267 return ObjectBusy;
2268
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;
2277
2278 save_state = SaveDC(graphics->hdc);
2279 EndPath(graphics->hdc);
2280 SelectObject(graphics->hdc, brush->gdibrush);
2281 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2282
2283 transform_and_round_points(graphics, pti, ptf, 4);
2284
2285 Polygon(graphics->hdc, pti, 4);
2286
2287 RestoreDC(graphics->hdc, save_state);
2288
2289 return Ok;
2290 }
2291
2292 GpStatus WINGDIPAPI GdipFillRectangleI(GpGraphics *graphics, GpBrush *brush,
2293 INT x, INT y, INT width, INT height)
2294 {
2295 INT save_state;
2296 GpPointF ptf[4];
2297 POINT pti[4];
2298
2299 TRACE("(%p, %p, %d, %d, %d, %d)\n", graphics, brush, x, y, width, height);
2300
2301 if(!graphics || !brush)
2302 return InvalidParameter;
2303
2304 if(graphics->busy)
2305 return ObjectBusy;
2306
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;
2315
2316 save_state = SaveDC(graphics->hdc);
2317 EndPath(graphics->hdc);
2318 SelectObject(graphics->hdc, brush->gdibrush);
2319 SelectObject(graphics->hdc, GetStockObject(NULL_PEN));
2320
2321 transform_and_round_points(graphics, pti, ptf, 4);
2322
2323 Polygon(graphics->hdc, pti, 4);
2324
2325 RestoreDC(graphics->hdc, save_state);
2326
2327 return Ok;
2328 }
2329
2330 GpStatus WINGDIPAPI GdipFillRectangles(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRectF *rects,
2331 INT count)
2332 {
2333 GpStatus ret;
2334 INT i;
2335
2336 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2337
2338 if(!rects)
2339 return InvalidParameter;
2340
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;
2344 }
2345
2346 return Ok;
2347 }
2348
2349 GpStatus WINGDIPAPI GdipFillRectanglesI(GpGraphics *graphics, GpBrush *brush, GDIPCONST GpRect *rects,
2350 INT count)
2351 {
2352 GpRectF *rectsF;
2353 GpStatus ret;
2354 INT i;
2355
2356 TRACE("(%p, %p, %p, %d)\n", graphics, brush, rects, count);
2357
2358 if(!rects || count <= 0)
2359 return InvalidParameter;
2360
2361 rectsF = GdipAlloc(sizeof(GpRectF)*count);
2362 if(!rectsF)
2363 return OutOfMemory;
2364
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;
2370 }
2371
2372 ret = GdipFillRectangles(graphics,brush,rectsF,count);
2373 GdipFree(rectsF);
2374
2375 return ret;
2376 }
2377
2378 GpStatus WINGDIPAPI GdipFillRegion(GpGraphics* graphics, GpBrush* brush,
2379 GpRegion* region)
2380 {
2381 if (!(graphics && brush && region))
2382 return InvalidParameter;
2383
2384 if(graphics->busy)
2385 return ObjectBusy;
2386
2387 FIXME("(%p, %p, %p): stub\n", graphics, brush, region);
2388
2389 return NotImplemented;
2390 }
2391
2392 GpStatus WINGDIPAPI GdipFlush(GpGraphics *graphics, GpFlushIntention intention)
2393 {
2394 static int calls;
2395
2396 if(!graphics)
2397 return InvalidParameter;
2398
2399 if(graphics->busy)
2400 return ObjectBusy;
2401
2402 if(!(calls++))
2403 FIXME("not implemented\n");
2404
2405 return NotImplemented;
2406 }
2407
2408 /* FIXME: Compositing mode is not used anywhere except the getter/setter. */
2409 GpStatus WINGDIPAPI GdipGetCompositingMode(GpGraphics *graphics,
2410 CompositingMode *mode)
2411 {
2412 TRACE("(%p, %p)\n", graphics, mode);
2413
2414 if(!graphics || !mode)
2415 return InvalidParameter;
2416
2417 if(graphics->busy)
2418 return ObjectBusy;
2419
2420 *mode = graphics->compmode;
2421
2422 return Ok;
2423 }
2424
2425 /* FIXME: Compositing quality is not used anywhere except the getter/setter. */
2426 GpStatus WINGDIPAPI GdipGetCompositingQuality(GpGraphics *graphics,
2427 CompositingQuality *quality)
2428 {
2429 TRACE("(%p, %p)\n", graphics, quality);
2430
2431 if(!graphics || !quality)
2432 return InvalidParameter;
2433
2434 if(graphics->busy)
2435 return ObjectBusy;
2436
2437 *quality = graphics->compqual;
2438
2439 return Ok;
2440 }
2441
2442 /* FIXME: Interpolation mode is not used anywhere except the getter/setter. */
2443 GpStatus WINGDIPAPI GdipGetInterpolationMode(GpGraphics *graphics,
2444 InterpolationMode *mode)
2445 {
2446 TRACE("(%p, %p)\n", graphics, mode);
2447
2448 if(!graphics || !mode)
2449 return InvalidParameter;
2450
2451 if(graphics->busy)
2452 return ObjectBusy;
2453
2454 *mode = graphics->interpolation;
2455
2456 return Ok;
2457 }
2458
2459 GpStatus WINGDIPAPI GdipGetNearestColor(GpGraphics *graphics, ARGB* argb)
2460 {
2461 if(!graphics || !argb)
2462 return InvalidParameter;
2463
2464 if(graphics->busy)
2465 return ObjectBusy;
2466
2467 FIXME("(%p, %p): stub\n", graphics, argb);
2468
2469 return NotImplemented;
2470 }
2471
2472 GpStatus WINGDIPAPI GdipGetPageScale(GpGraphics *graphics, REAL *scale)
2473 {
2474 TRACE("(%p, %p)\n", graphics, scale);
2475
2476 if(!graphics || !scale)
2477 return InvalidParameter;
2478
2479 if(graphics->busy)
2480 return ObjectBusy;
2481
2482 *scale = graphics->scale;
2483
2484 return Ok;
2485 }
2486
2487 GpStatus WINGDIPAPI GdipGetPageUnit(GpGraphics *graphics, GpUnit *unit)
2488 {
2489 TRACE("(%p, %p)\n", graphics, unit);
2490
2491 if(!graphics || !unit)
2492 return InvalidParameter;
2493
2494 if(graphics->busy)
2495 return ObjectBusy;
2496
2497 *unit = graphics->unit;
2498
2499 return Ok;
2500 }
2501
2502 /* FIXME: Pixel offset mode is not used anywhere except the getter/setter. */
2503 GpStatus WINGDIPAPI GdipGetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2504 *mode)
2505 {
2506 TRACE("(%p, %p)\n", graphics, mode);
2507
2508 if(!graphics || !mode)
2509 return InvalidParameter;
2510
2511 if(graphics->busy)
2512 return ObjectBusy;
2513
2514 *mode = graphics->pixeloffset;
2515
2516 return Ok;
2517 }
2518
2519 /* FIXME: Smoothing mode is not used anywhere except the getter/setter. */
2520 GpStatus WINGDIPAPI GdipGetSmoothingMode(GpGraphics *graphics, SmoothingMode *mode)
2521 {
2522 TRACE("(%p, %p)\n", graphics, mode);
2523
2524 if(!graphics || !mode)
2525 return InvalidParameter;
2526
2527 if(graphics->busy)
2528 return ObjectBusy;
2529
2530 *mode = graphics->smoothing;
2531
2532 return Ok;
2533 }
2534
2535 GpStatus WINGDIPAPI GdipGetTextContrast(GpGraphics *graphics, UINT *contrast)
2536 {
2537 TRACE("(%p, %p)\n", graphics, contrast);
2538
2539 if(!graphics || !contrast)
2540 return InvalidParameter;
2541
2542 *contrast = graphics->textcontrast;
2543
2544 return Ok;
2545 }
2546
2547 /* FIXME: Text rendering hint is not used anywhere except the getter/setter. */
2548 GpStatus WINGDIPAPI GdipGetTextRenderingHint(GpGraphics *graphics,
2549 TextRenderingHint *hint)
2550 {
2551 TRACE("(%p, %p)\n", graphics, hint);
2552
2553 if(!graphics || !hint)
2554 return InvalidParameter;
2555
2556 if(graphics->busy)
2557 return ObjectBusy;
2558
2559 *hint = graphics->texthint;
2560
2561 return Ok;
2562 }
2563
2564 GpStatus WINGDIPAPI GdipGetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
2565 {
2566 TRACE("(%p, %p)\n", graphics, matrix);
2567
2568 if(!graphics || !matrix)
2569 return InvalidParameter;
2570
2571 if(graphics->busy)
2572 return ObjectBusy;
2573
2574 *matrix = *graphics->worldtrans;
2575 return Ok;
2576 }
2577
2578 GpStatus WINGDIPAPI GdipGraphicsClear(GpGraphics *graphics, ARGB color)
2579 {
2580 GpSolidFill *brush;
2581 GpStatus stat;
2582 RECT rect;
2583
2584 TRACE("(%p, %x)\n", graphics, color);
2585
2586 if(!graphics)
2587 return InvalidParameter;
2588
2589 if(graphics->busy)
2590 return ObjectBusy;
2591
2592 if((stat = GdipCreateSolidFill(color, &brush)) != Ok)
2593 return stat;
2594
2595 if(graphics->hwnd){
2596 if(!GetWindowRect(graphics->hwnd, &rect)){
2597 GdipDeleteBrush((GpBrush*)brush);
2598 return GenericError;
2599 }
2600
2601 GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)(rect.right - rect.left),
2602 (REAL)(rect.bottom - rect.top));
2603 }
2604 else
2605 GdipFillRectangle(graphics, (GpBrush*)brush, 0.0, 0.0, (REAL)GetDeviceCaps(graphics->hdc, HORZRES),
2606 (REAL)GetDeviceCaps(graphics->hdc, VERTRES));
2607
2608 GdipDeleteBrush((GpBrush*)brush);
2609
2610 return Ok;
2611 }
2612
2613 GpStatus WINGDIPAPI GdipIsClipEmpty(GpGraphics *graphics, BOOL *res)
2614 {
2615 TRACE("(%p, %p)\n", graphics, res);
2616
2617 if(!graphics || !res)
2618 return InvalidParameter;
2619
2620 return GdipIsEmptyRegion(graphics->clip, graphics, res);
2621 }
2622
2623 GpStatus WINGDIPAPI GdipIsVisiblePoint(GpGraphics *graphics, REAL x, REAL y, BOOL *result)
2624 {
2625 FIXME("(%p, %.2f, %.2f, %p) stub\n", graphics, x, y, result);
2626
2627 if(!graphics || !result)
2628 return InvalidParameter;
2629
2630 if(graphics->busy)
2631 return ObjectBusy;
2632
2633 return NotImplemented;
2634 }
2635
2636 GpStatus WINGDIPAPI GdipIsVisiblePointI(GpGraphics *graphics, INT x, INT y, BOOL *result)
2637 {
2638 FIXME("(%p, %d, %d, %p) stub\n", graphics, x, y, result);
2639
2640 if(!graphics || !result)
2641 return InvalidParameter;
2642
2643 if(graphics->busy)
2644 return ObjectBusy;
2645
2646 return NotImplemented;
2647 }
2648
2649 GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics* graphics,
2650 GDIPCONST WCHAR* string, INT length, GDIPCONST GpFont* font,
2651 GDIPCONST RectF* layoutRect, GDIPCONST GpStringFormat *stringFormat,
2652 INT regionCount, GpRegion** regions)
2653 {
2654 if (!(graphics && string && font && layoutRect && stringFormat && regions))
2655 return InvalidParameter;
2656
2657 FIXME("stub: %p %s %d %p %p %p %d %p\n", graphics, debugstr_w(string),
2658 length, font, layoutRect, stringFormat, regionCount, regions);
2659
2660 return NotImplemented;
2661 }
2662
2663 /* Find the smallest rectangle that bounds the text when it is printed in rect
2664 * according to the format options listed in format. If rect has 0 width and
2665 * height, then just find the smallest rectangle that bounds the text when it's
2666 * printed at location (rect->X, rect-Y). */
2667 GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics,
2668 GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font,
2669 GDIPCONST RectF *rect, GDIPCONST GpStringFormat *format, RectF *bounds,
2670 INT *codepointsfitted, INT *linesfilled)
2671 {
2672 HFONT oldfont;
2673 WCHAR* stringdup;
2674 INT sum = 0, height = 0, fit, fitcpy, max_width = 0, i, j, lret, nwidth,
2675 nheight;
2676 SIZE size;
2677
2678 if(!graphics || !string || !font || !rect)
2679 return InvalidParameter;
2680
2681 if(linesfilled) *linesfilled = 0;
2682 if(codepointsfitted) *codepointsfitted = 0;
2683
2684 if(format)
2685 TRACE("may be ignoring some format flags: attr %x\n", format->attr);
2686
2687 if(length == -1) length = lstrlenW(string);
2688
2689 stringdup = GdipAlloc((length + 1) * sizeof(WCHAR));
2690 if(!stringdup) return OutOfMemory;
2691
2692 oldfont = SelectObject(graphics->hdc, CreateFontIndirectW(&font->lfw));
2693 nwidth = roundr(rect->Width);
2694 nheight = roundr(rect->Height);
2695
2696 if((nwidth == 0) && (nheight == 0))
2697 nwidth = nheight = INT_MAX;
2698
2699 for(i = 0, j = 0; i < length; i++){
2700 if(!isprint(string[i]) && (string[i] != '\n'))
2701 continue;
2702
2703 stringdup[j] = string[i];
2704 j++;
2705 }
2706
2707 stringdup[j] = 0;
2708 length = j;
2709
2710 while(sum < length){
2711 GetTextExtentExPointW(graphics->hdc, stringdup + sum, length - sum,
2712 nwidth, &fit, NULL, &size);
2713 fitcpy = fit;
2714
2715 if(fit == 0)
2716 break;
2717
2718 for(lret = 0; lret < fit; lret++)
2719 if(*(stringdup + sum + lret) == '\n')
2720 break;
2721
2722 /* Line break code (may look strange, but it imitates windows). */
2723 if(lret < fit)
2724 fit = lret; /* this is not an off-by-one error */
2725 else if(fit < (length - sum)){
2726 if(*(stringdup + sum + fit) == ' ')
2727 while(*(stringdup + sum + fit) == ' ')
2728 fit++;
2729 else
2730 while(*(stringdup + sum + fit - 1) != ' '){
2731 fit--;
2732
2733 if(*(stringdup + sum + fit) == '\t')
2734 break;
2735
2736 if(fit == 0){
2737 fit = fitcpy;
2738 break;
2739 }
2740 }
2741 }
2742
2743 GetTextExtentExPointW(graphics->hdc, stringdup + sum, fit,
2744 nwidth, &j, NULL, &size);
2745
2746 sum += fit + (lret < fitcpy ? 1 : 0);
2747 if(codepointsfitted) *codepointsfitted = sum;
2748
2749 height += size.cy;
2750 if(linesfilled) *linesfilled += size.cy;
2751 max_width = max(max_width, size.cx);
2752
2753 if(height > nheight)
2754 break;
2755
2756 /* Stop if this was a linewrap (but not if it was a linebreak). */
2757 if((lret == fitcpy) && format && (format->attr & StringFormatFlagsNoWrap))
2758 break;
2759 }
2760
2761 bounds->X = rect->X;
2762 bounds->Y = rect->Y;
2763 bounds->Width = (REAL)max_width;
2764 bounds->Height = (REAL) min(height, nheight);
2765
2766 GdipFree(stringdup);
2767 DeleteObject(SelectObject(graphics->hdc, oldfont));
2768
2769 return Ok;
2770 }
2771
2772 GpStatus WINGDIPAPI GdipResetClip(GpGraphics *graphics)
2773 {
2774 TRACE("(%p)\n", graphics);
2775
2776 if(!graphics)
2777 return InvalidParameter;
2778
2779 if(graphics->busy)
2780 return ObjectBusy;
2781
2782 return GdipSetInfinite(graphics->clip);
2783 }
2784
2785 GpStatus WINGDIPAPI GdipResetWorldTransform(GpGraphics *graphics)
2786 {
2787 TRACE("(%p)\n", graphics);
2788
2789 if(!graphics)
2790 return InvalidParameter;
2791
2792 if(graphics->busy)
2793 return ObjectBusy;
2794
2795 graphics->worldtrans->matrix[0] = 1.0;
2796 graphics->worldtrans->matrix[1] = 0.0;
2797 graphics->worldtrans->matrix[2] = 0.0;
2798 graphics->worldtrans->matrix[3] = 1.0;
2799 graphics->worldtrans->matrix[4] = 0.0;
2800 graphics->worldtrans->matrix[5] = 0.0;
2801
2802 return Ok;
2803 }
2804
2805 GpStatus WINGDIPAPI GdipRestoreGraphics(GpGraphics *graphics, GraphicsState state)
2806 {
2807 static int calls;
2808
2809 if(!graphics)
2810 return InvalidParameter;
2811
2812 if(!(calls++))
2813 FIXME("graphics state not implemented\n");
2814
2815 return Ok;
2816 }
2817
2818 GpStatus WINGDIPAPI GdipRotateWorldTransform(GpGraphics *graphics, REAL angle,
2819 GpMatrixOrder order)
2820 {
2821 TRACE("(%p, %.2f, %d)\n", graphics, angle, order);
2822
2823 if(!graphics)
2824 return InvalidParameter;
2825
2826 if(graphics->busy)
2827 return ObjectBusy;
2828
2829 return GdipRotateMatrix(graphics->worldtrans, angle, order);
2830 }
2831
2832 GpStatus WINGDIPAPI GdipSaveGraphics(GpGraphics *graphics, GraphicsState *state)
2833 {
2834 static int calls;
2835
2836 if(!graphics || !state)
2837 return InvalidParameter;
2838
2839 if(!(calls++))
2840 FIXME("graphics state not implemented\n");
2841
2842 *state = 0xdeadbeef;
2843 return Ok;
2844 }
2845
2846 GpStatus WINGDIPAPI GdipBeginContainer2(GpGraphics *graphics, GraphicsContainer *state)
2847 {
2848 FIXME("(%p, %p)\n", graphics, state);
2849
2850 if(!graphics || !state)
2851 return InvalidParameter;
2852
2853 *state = 0xdeadbeef;
2854 return Ok;
2855 }
2856
2857 GpStatus WINGDIPAPI GdipEndContainer(GpGraphics *graphics, GraphicsState state)
2858 {
2859 FIXME("(%p, 0x%x)\n", graphics, state);
2860
2861 if(!graphics || !state)
2862 return InvalidParameter;
2863
2864 return Ok;
2865 }
2866
2867 GpStatus WINGDIPAPI GdipScaleWorldTransform(GpGraphics *graphics, REAL sx,
2868 REAL sy, GpMatrixOrder order)
2869 {
2870 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, sx, sy, order);
2871
2872 if(!graphics)
2873 return InvalidParameter;
2874
2875 if(graphics->busy)
2876 return ObjectBusy;
2877
2878 return GdipScaleMatrix(graphics->worldtrans, sx, sy, order);
2879 }
2880
2881 GpStatus WINGDIPAPI GdipSetClipGraphics(GpGraphics *graphics, GpGraphics *srcgraphics,
2882 CombineMode mode)
2883 {
2884 TRACE("(%p, %p, %d)\n", graphics, srcgraphics, mode);
2885
2886 if(!graphics || !srcgraphics)
2887 return InvalidParameter;
2888
2889 return GdipCombineRegionRegion(graphics->clip, srcgraphics->clip, mode);
2890 }
2891
2892 GpStatus WINGDIPAPI GdipSetCompositingMode(GpGraphics *graphics,
2893 CompositingMode mode)
2894 {
2895 TRACE("(%p, %d)\n", graphics, mode);
2896
2897 if(!graphics)
2898 return InvalidParameter;
2899
2900 if(graphics->busy)
2901 return ObjectBusy;
2902
2903 graphics->compmode = mode;
2904
2905 return Ok;
2906 }
2907
2908 GpStatus WINGDIPAPI GdipSetCompositingQuality(GpGraphics *graphics,
2909 CompositingQuality quality)
2910 {
2911 TRACE("(%p, %d)\n", graphics, quality);
2912
2913 if(!graphics)
2914 return InvalidParameter;
2915
2916 if(graphics->busy)
2917 return ObjectBusy;
2918
2919 graphics->compqual = quality;
2920
2921 return Ok;
2922 }
2923
2924 GpStatus WINGDIPAPI GdipSetInterpolationMode(GpGraphics *graphics,
2925 InterpolationMode mode)
2926 {
2927 TRACE("(%p, %d)\n", graphics, mode);
2928
2929 if(!graphics)
2930 return InvalidParameter;
2931
2932 if(graphics->busy)
2933 return ObjectBusy;
2934
2935 graphics->interpolation = mode;
2936
2937 return Ok;
2938 }
2939
2940 GpStatus WINGDIPAPI GdipSetPageScale(GpGraphics *graphics, REAL scale)
2941 {
2942 TRACE("(%p, %.2f)\n", graphics, scale);
2943
2944 if(!graphics || (scale <= 0.0))
2945 return InvalidParameter;
2946
2947 if(graphics->busy)
2948 return ObjectBusy;
2949
2950 graphics->scale = scale;
2951
2952 return Ok;
2953 }
2954
2955 GpStatus WINGDIPAPI GdipSetPageUnit(GpGraphics *graphics, GpUnit unit)
2956 {
2957 TRACE("(%p, %d)\n", graphics, unit);
2958
2959 if(!graphics)
2960 return InvalidParameter;
2961
2962 if(graphics->busy)
2963 return ObjectBusy;
2964
2965 if(unit == UnitWorld)
2966 return InvalidParameter;
2967
2968 graphics->unit = unit;
2969
2970 return Ok;
2971 }
2972
2973 GpStatus WINGDIPAPI GdipSetPixelOffsetMode(GpGraphics *graphics, PixelOffsetMode
2974 mode)
2975 {
2976 TRACE("(%p, %d)\n", graphics, mode);
2977
2978 if(!graphics)
2979 return InvalidParameter;
2980
2981 if(graphics->busy)
2982 return ObjectBusy;
2983
2984 graphics->pixeloffset = mode;
2985
2986 return Ok;
2987 }
2988
2989 GpStatus WINGDIPAPI GdipSetSmoothingMode(GpGraphics *graphics, SmoothingMode mode)
2990 {
2991 TRACE("(%p, %d)\n", graphics, mode);
2992
2993 if(!graphics)
2994 return InvalidParameter;
2995
2996 if(graphics->busy)
2997 return ObjectBusy;
2998
2999 graphics->smoothing = mode;
3000
3001 return Ok;
3002 }
3003
3004 GpStatus WINGDIPAPI GdipSetTextContrast(GpGraphics *graphics, UINT contrast)
3005 {
3006 TRACE("(%p, %d)\n", graphics, contrast);
3007
3008 if(!graphics)
3009 return InvalidParameter;
3010
3011 graphics->textcontrast = contrast;
3012
3013 return Ok;
3014 }
3015
3016 GpStatus WINGDIPAPI GdipSetTextRenderingHint(GpGraphics *graphics,
3017 TextRenderingHint hint)
3018 {
3019 TRACE("(%p, %d)\n", graphics, hint);
3020
3021 if(!graphics)
3022 return InvalidParameter;
3023
3024 if(graphics->busy)
3025 return ObjectBusy;
3026
3027 graphics->texthint = hint;
3028
3029 return Ok;
3030 }
3031
3032 GpStatus WINGDIPAPI GdipSetWorldTransform(GpGraphics *graphics, GpMatrix *matrix)
3033 {
3034 TRACE("(%p, %p)\n", graphics, matrix);
3035
3036 if(!graphics || !matrix)
3037 return InvalidParameter;
3038
3039 if(graphics->busy)
3040 return ObjectBusy;
3041
3042 GdipDeleteMatrix(graphics->worldtrans);
3043 return GdipCloneMatrix(matrix, &graphics->worldtrans);
3044 }
3045
3046 GpStatus WINGDIPAPI GdipTranslateWorldTransform(GpGraphics *graphics, REAL dx,
3047 REAL dy, GpMatrixOrder order)
3048 {
3049 TRACE("(%p, %.2f, %.2f, %d)\n", graphics, dx, dy, order);
3050
3051 if(!graphics)
3052 return InvalidParameter;
3053
3054 if(graphics->busy)
3055 return ObjectBusy;
3056
3057 return GdipTranslateMatrix(graphics->worldtrans, dx, dy, order);
3058 }
3059
3060 GpStatus WINGDIPAPI GdipSetClipPath(GpGraphics *graphics, GpPath *path, CombineMode mode)
3061 {
3062 TRACE("(%p, %p, %d)\n", graphics, path, mode);
3063
3064 if(!graphics)
3065 return InvalidParameter;
3066
3067 if(graphics->busy)
3068 return ObjectBusy;
3069
3070 return GdipCombineRegionPath(graphics->clip, path, mode);
3071 }
3072
3073 GpStatus WINGDIPAPI GdipSetClipRect(GpGraphics *graphics, REAL x, REAL y,
3074 REAL width, REAL height,
3075 CombineMode mode)
3076 {
3077 GpRectF rect;
3078
3079 TRACE("(%p, %.2f, %.2f, %.2f, %.2f, %d)\n", graphics, x, y, width, height, mode);
3080
3081 if(!graphics)
3082 return InvalidParameter;
3083
3084 if(graphics->busy)
3085 return ObjectBusy;
3086
3087 rect.X = x;
3088 rect.Y = y;
3089 rect.Width = width;
3090 rect.Height = height;
3091
3092 return GdipCombineRegionRect(graphics->clip, &rect, mode);
3093 }
3094
3095 GpStatus WINGDIPAPI GdipSetClipRectI(GpGraphics *graphics, INT x, INT y,
3096 INT width, INT height,
3097 CombineMode mode)
3098 {
3099 TRACE("(%p, %d, %d, %d, %d, %d)\n", graphics, x, y, width, height, mode);
3100
3101 if(!graphics)
3102 return InvalidParameter;
3103
3104 if(graphics->busy)
3105 return ObjectBusy;
3106
3107 return GdipSetClipRect(graphics, (REAL)x, (REAL)y, (REAL)width, (REAL)height, mode);
3108 }
3109
3110 GpStatus WINGDIPAPI GdipSetClipRegion(GpGraphics *graphics, GpRegion *region,
3111 CombineMode mode)
3112 {
3113 TRACE("(%p, %p, %d)\n", graphics, region, mode);
3114
3115 if(!graphics || !region)
3116 return InvalidParameter;
3117
3118 if(graphics->busy)
3119 return ObjectBusy;
3120
3121 return GdipCombineRegionRegion(graphics->clip, region, mode);
3122 }
3123
3124 GpStatus WINGDIPAPI GdipSetMetafileDownLevelRasterizationLimit(GpMetafile *metafile,
3125 UINT limitDpi)
3126 {
3127 static int calls;
3128
3129 if(!(calls++))
3130 FIXME("not implemented\n");
3131
3132 return NotImplemented;
3133 }
3134
3135 GpStatus WINGDIPAPI GdipDrawPolygon(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPointF *points,
3136 INT count)
3137 {
3138 INT save_state;
3139 POINT *pti;
3140
3141 TRACE("(%p, %p, %d)\n", graphics, points, count);
3142
3143 if(!graphics || !pen || count<=0)
3144 return InvalidParameter;
3145
3146 if(graphics->busy)
3147 return ObjectBusy;
3148
3149 pti = GdipAlloc(sizeof(POINT) * count);
3150
3151 save_state = prepare_dc(graphics, pen);
3152 SelectObject(graphics->hdc, GetStockObject(NULL_BRUSH));
3153
3154 transform_and_round_points(graphics, pti, (GpPointF*)points, count);
3155 Polygon(graphics->hdc, pti, count);
3156
3157 restore_dc(graphics, save_state);
3158 GdipFree(pti);
3159
3160 return Ok;
3161 }
3162
3163 GpStatus WINGDIPAPI GdipDrawPolygonI(GpGraphics *graphics,GpPen *pen,GDIPCONST GpPoint *points,
3164 INT count)
3165 {
3166 GpStatus ret;
3167 GpPointF *ptf;
3168 INT i;
3169
3170 TRACE("(%p, %p, %p, %d)\n", graphics, pen, points, count);
3171
3172 if(count<=0) return InvalidParameter;
3173 ptf = GdipAlloc(sizeof(GpPointF) * count);
3174
3175 for(i = 0;i < count; i++){
3176 ptf[i].X = (REAL)points[i].X;
3177 ptf[i].Y = (REAL)points[i].Y;
3178 }
3179
3180 ret = GdipDrawPolygon(graphics,pen,ptf,count);
3181 GdipFree(ptf);
3182
3183 return ret;
3184 }
3185
3186 GpStatus WINGDIPAPI GdipGetDpiX(GpGraphics *graphics, REAL* dpi)
3187 {
3188 TRACE("(%p, %p)\n", graphics, dpi);
3189
3190 if(!graphics || !dpi)
3191 return InvalidParameter;
3192
3193 if(graphics->busy)
3194 return ObjectBusy;
3195
3196 *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSX);
3197
3198 return Ok;
3199 }
3200
3201 GpStatus WINGDIPAPI GdipGetDpiY(GpGraphics *graphics, REAL* dpi)
3202 {
3203 TRACE("(%p, %p)\n", graphics, dpi);
3204
3205 if(!graphics || !dpi)
3206 return InvalidParameter;
3207
3208 if(graphics->busy)
3209 return ObjectBusy;
3210
3211 *dpi = (REAL)GetDeviceCaps(graphics->hdc, LOGPIXELSY);
3212
3213 return Ok;
3214 }
3215
3216 GpStatus WINGDIPAPI GdipMultiplyWorldTransform(GpGraphics *graphics, GDIPCONST GpMatrix *matrix,
3217 GpMatrixOrder order)
3218 {
3219 GpMatrix m;
3220 GpStatus ret;
3221
3222 TRACE("(%p, %p, %d)\n", graphics, matrix, order);
3223
3224 if(!graphics || !matrix)
3225 return InvalidParameter;
3226
3227 if(graphics->busy)
3228 return ObjectBusy;
3229
3230 m = *(graphics->worldtrans);
3231
3232 ret = GdipMultiplyMatrix(&m, (GpMatrix*)matrix, order);
3233 if(ret == Ok)
3234 *(graphics->worldtrans) = m;
3235
3236 return ret;
3237 }
3238
3239 GpStatus WINGDIPAPI GdipGetDC(GpGraphics *graphics, HDC *hdc)
3240 {
3241 TRACE("(%p, %p)\n", graphics, hdc);
3242
3243 if(!graphics || !hdc)
3244 return InvalidParameter;
3245
3246 if(graphics->busy)
3247 return ObjectBusy;
3248
3249 *hdc = graphics->hdc;
3250 graphics->busy = TRUE;
3251
3252 return Ok;
3253 }
3254
3255 GpStatus WINGDIPAPI GdipReleaseDC(GpGraphics *graphics, HDC hdc)
3256 {
3257 TRACE("(%p, %p)\n", graphics, hdc);
3258
3259 if(!graphics)
3260 return InvalidParameter;
3261
3262 if(graphics->hdc != hdc || !(graphics->busy))
3263 return InvalidParameter;
3264
3265 graphics->busy = FALSE;
3266
3267 return Ok;
3268 }
3269
3270 GpStatus WINGDIPAPI GdipGetClip(GpGraphics *graphics, GpRegion *region)
3271 {
3272 GpRegion *clip;
3273 GpStatus status;
3274
3275 TRACE("(%p, %p)\n", graphics, region);
3276
3277 if(!graphics || !region)
3278 return InvalidParameter;
3279
3280 if(graphics->busy)
3281 return ObjectBusy;
3282
3283 if((status = GdipCloneRegion(graphics->clip, &clip)) != Ok)
3284 return status;
3285
3286 /* free everything except root node and header */
3287 delete_element(&region->node);
3288 memcpy(region, clip, sizeof(GpRegion));
3289
3290 return Ok;
3291 }
3292
3293 GpStatus WINGDIPAPI GdipTransformPoints(GpGraphics *graphics, GpCoordinateSpace dst_space,
3294 GpCoordinateSpace src_space, GpPointF *points, INT count)
3295 {
3296 if(!graphics || !points || count <= 0)
3297 return InvalidParameter;
3298
3299 if(graphics->busy)
3300 return ObjectBusy;
3301
3302 FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3303
3304 return NotImplemented;
3305 }
3306
3307 GpStatus WINGDIPAPI GdipTransformPointsI(GpGraphics *graphics, GpCoordinateSpace dst_space,
3308 GpCoordinateSpace src_space, GpPoint *points, INT count)
3309 {
3310 FIXME("(%p, %d, %d, %p, %d): stub\n", graphics, dst_space, src_space, points, count);
3311
3312 return NotImplemented;
3313 }
3314
3315 HPALETTE WINGDIPAPI GdipCreateHalftonePalette(void)
3316 {
3317 FIXME("\n");
3318
3319 return NULL;
3320 }