434518a940b861311ac1ad345745a62ef73830b3
[reactos.git] / reactos / win32ss / gdi / ntgdi / path.c
1 /*
2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: subsystems/win32/win32k/objects/path.c
5 * PURPOSE: Graphics paths (BeginPath, EndPath etc.)
6 * PROGRAMMER: Copyright 1997, 1998 Martin Boehme
7 * 1999 Huw D M Davies
8 * 2005 Dmitry Timoshkov
9 */
10
11 #include <win32k.h>
12 #include <suppress.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #ifdef _MSC_VER
18 #pragma warning(disable:4244)
19 #endif
20
21 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
22 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
23 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
24
25 /***********************************************************************
26 * Internal functions
27 */
28
29 /* PATH_DestroyGdiPath
30 *
31 * Destroys a GdiPath structure (frees the memory in the arrays).
32 */
33 VOID
34 FASTCALL
35 PATH_DestroyGdiPath(PPATH pPath)
36 {
37 ASSERT(pPath != NULL);
38
39 if (pPath->pPoints) ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
40 if (pPath->pFlags) ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
41 }
42
43 BOOL
44 FASTCALL
45 PATH_Delete(HPATH hPath)
46 {
47 PPATH pPath;
48 if (!hPath) return FALSE;
49 pPath = PATH_LockPath(hPath);
50 if (!pPath) return FALSE;
51 PATH_DestroyGdiPath(pPath);
52 GDIOBJ_vDeleteObject(&pPath->BaseObject);
53 return TRUE;
54 }
55
56
57 VOID
58 FASTCALL
59 IntGdiCloseFigure(PPATH pPath)
60 {
61 ASSERT(pPath->state == PATH_Open);
62
63 // FIXME: Shouldn't we draw a line to the beginning of the figure?
64 // Set PT_CLOSEFIGURE on the last entry and start a new stroke
65 if (pPath->numEntriesUsed)
66 {
67 pPath->pFlags[pPath->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
68 pPath->newStroke = TRUE;
69 }
70 }
71
72 /* MSDN: This fails if the device coordinates exceed 27 bits, or if the converted
73 logical coordinates exceed 32 bits. */
74 BOOL
75 FASTCALL
76 GdiPathDPtoLP(
77 PDC pdc,
78 PPOINT ppt,
79 INT count)
80 {
81 XFORMOBJ xo;
82
83 XFORMOBJ_vInit(&xo, &pdc->pdcattr->mxDeviceToWorld);
84 return XFORMOBJ_bApplyXform(&xo, XF_LTOL, count, (PPOINTL)ppt, (PPOINTL)ppt);
85 }
86
87 /* PATH_FillPath
88 *
89 *
90 */
91 BOOL
92 FASTCALL
93 PATH_FillPath(
94 PDC dc,
95 PPATH pPath)
96 {
97 //INT mapMode, graphicsMode;
98 //SIZE ptViewportExt, ptWindowExt;
99 //POINTL ptViewportOrg, ptWindowOrg;
100 XFORM xform;
101 HRGN hrgn;
102 PDC_ATTR pdcattr = dc->pdcattr;
103
104 if (pPath->state != PATH_Closed)
105 {
106 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
107 return FALSE;
108 }
109
110 if (PATH_PathToRegion(pPath, pdcattr->jFillMode, &hrgn))
111 {
112 /* Since PaintRgn interprets the region as being in logical coordinates
113 * but the points we store for the path are already in device
114 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
115 * Using SaveDC to save information about the mapping mode / world
116 * transform would be easier but would require more overhead, especially
117 * now that SaveDC saves the current path.
118 */
119
120 /* Save the information about the old mapping mode */
121 //mapMode = pdcattr->iMapMode;
122 //ptViewportExt = pdcattr->szlViewportExt;
123 //ptViewportOrg = pdcattr->ptlViewportOrg;
124 //ptWindowExt = pdcattr->szlWindowExt;
125 //ptWindowOrg = pdcattr->ptlWindowOrg;
126
127 /* Save world transform
128 * NB: The Windows documentation on world transforms would lead one to
129 * believe that this has to be done only in GM_ADVANCED; however, my
130 * tests show that resetting the graphics mode to GM_COMPATIBLE does
131 * not reset the world transform.
132 */
133 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
134
135 /* Set MM_TEXT */
136 // IntGdiSetMapMode(dc, MM_TEXT);
137 // pdcattr->ptlViewportOrg.x = 0;
138 // pdcattr->ptlViewportOrg.y = 0;
139 // pdcattr->ptlWindowOrg.x = 0;
140 // pdcattr->ptlWindowOrg.y = 0;
141
142 // graphicsMode = pdcattr->iGraphicsMode;
143 // pdcattr->iGraphicsMode = GM_ADVANCED;
144 // IntGdiModifyWorldTransform(dc, &xform, MWT_IDENTITY);
145 // pdcattr->iGraphicsMode = graphicsMode;
146
147 /* Paint the region */
148 IntGdiPaintRgn(dc, hrgn);
149 GreDeleteObject(hrgn);
150 /* Restore the old mapping mode */
151 // IntGdiSetMapMode(dc, mapMode);
152 // pdcattr->szlViewportExt = ptViewportExt;
153 // pdcattr->ptlViewportOrg = ptViewportOrg;
154 // pdcattr->szlWindowExt = ptWindowExt;
155 // pdcattr->ptlWindowOrg = ptWindowOrg;
156
157 /* Go to GM_ADVANCED temporarily to restore the world transform */
158 //graphicsMode = pdcattr->iGraphicsMode;
159 // pdcattr->iGraphicsMode = GM_ADVANCED;
160 // IntGdiModifyWorldTransform(dc, &xform, MWT_MAX+1);
161 // pdcattr->iGraphicsMode = graphicsMode;
162 return TRUE;
163 }
164 return FALSE;
165 }
166
167 /* PATH_InitGdiPath
168 *
169 * Initializes the GdiPath structure.
170 */
171 VOID
172 FASTCALL
173 PATH_InitGdiPath(
174 PPATH pPath)
175 {
176 ASSERT(pPath != NULL);
177
178 pPath->state = PATH_Null;
179 pPath->pPoints = NULL;
180 pPath->pFlags = NULL;
181 pPath->numEntriesUsed = 0;
182 pPath->numEntriesAllocated = 0;
183 }
184
185 /* PATH_AssignGdiPath
186 *
187 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
188 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
189 * not just the pointers. Since this means that the arrays in pPathDest may
190 * need to be resized, pPathDest should have been initialized using
191 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
192 * not a copy constructor).
193 * Returns TRUE if successful, else FALSE.
194 */
195 BOOL
196 FASTCALL
197 PATH_AssignGdiPath(
198 PPATH pPathDest,
199 const PPATH pPathSrc)
200 {
201 ASSERT(pPathDest != NULL && pPathSrc != NULL);
202
203 /* Make sure destination arrays are big enough */
204 if (!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
205 return FALSE;
206
207 /* Perform the copy operation */
208 memcpy(pPathDest->pPoints, pPathSrc->pPoints,
209 sizeof(POINT)*pPathSrc->numEntriesUsed);
210 memcpy(pPathDest->pFlags, pPathSrc->pFlags,
211 sizeof(BYTE)*pPathSrc->numEntriesUsed);
212
213 pPathDest->state = pPathSrc->state;
214 pPathDest->numEntriesUsed = pPathSrc->numEntriesUsed;
215 pPathDest->newStroke = pPathSrc->newStroke;
216 return TRUE;
217 }
218
219 /* PATH_MoveTo
220 *
221 * Should be called when a MoveTo is performed on a DC that has an
222 * open path. This starts a new stroke. Returns TRUE if successful, else
223 * FALSE.
224 */
225 BOOL
226 FASTCALL
227 PATH_MoveTo(
228 PDC dc)
229 {
230 PPATH pPath = PATH_LockPath(dc->dclevel.hPath);
231 if (!pPath) return FALSE;
232
233 /* Check that path is open */
234 if (pPath->state != PATH_Open)
235 {
236 PATH_UnlockPath(pPath);
237 /* FIXME: Do we have to call SetLastError? */
238 return FALSE;
239 }
240 /* Start a new stroke */
241 pPath->newStroke = TRUE;
242 PATH_UnlockPath(pPath);
243 return TRUE;
244 }
245
246 /* PATH_LineTo
247 *
248 * Should be called when a LineTo is performed on a DC that has an
249 * open path. This adds a PT_LINETO entry to the path (and possibly
250 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
251 * Returns TRUE if successful, else FALSE.
252 */
253 BOOL
254 FASTCALL
255 PATH_LineTo(
256 PDC dc,
257 INT x,
258 INT y)
259 {
260 BOOL Ret;
261 PPATH pPath;
262 POINT point, pointCurPos;
263
264 pPath = PATH_LockPath(dc->dclevel.hPath);
265 if (!pPath) return FALSE;
266
267 /* Check that path is open */
268 if (pPath->state != PATH_Open)
269 {
270 PATH_UnlockPath(pPath);
271 return FALSE;
272 }
273
274 /* Convert point to device coordinates */
275 point.x = x;
276 point.y = y;
277 CoordLPtoDP(dc, &point);
278
279 /* Add a PT_MOVETO if necessary */
280 if (pPath->newStroke)
281 {
282 pPath->newStroke = FALSE;
283 IntGetCurrentPositionEx(dc, &pointCurPos);
284 CoordLPtoDP(dc, &pointCurPos);
285 if (!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
286 {
287 PATH_UnlockPath(pPath);
288 return FALSE;
289 }
290 }
291
292 /* Add a PT_LINETO entry */
293 Ret = PATH_AddEntry(pPath, &point, PT_LINETO);
294 PATH_UnlockPath(pPath);
295 return Ret;
296 }
297
298 /* PATH_Rectangle
299 *
300 * Should be called when a call to Rectangle is performed on a DC that has
301 * an open path. Returns TRUE if successful, else FALSE.
302 */
303 BOOL
304 FASTCALL
305 PATH_Rectangle(
306 PDC dc,
307 INT x1,
308 INT y1,
309 INT x2,
310 INT y2)
311 {
312 PPATH pPath;
313 POINT corners[2], pointTemp;
314 INT temp;
315
316 pPath = PATH_LockPath(dc->dclevel.hPath);
317 if (!pPath) return FALSE;
318
319 /* Check that path is open */
320 if (pPath->state != PATH_Open)
321 {
322 PATH_UnlockPath(pPath);
323 return FALSE;
324 }
325
326 /* Convert points to device coordinates */
327 corners[0].x = x1;
328 corners[0].y = y1;
329 corners[1].x = x2;
330 corners[1].y = y2;
331 IntLPtoDP(dc, corners, 2);
332
333 /* Make sure first corner is top left and second corner is bottom right */
334 if (corners[0].x > corners[1].x)
335 {
336 temp = corners[0].x;
337 corners[0].x = corners[1].x;
338 corners[1].x = temp;
339 }
340 if (corners[0].y > corners[1].y)
341 {
342 temp = corners[0].y;
343 corners[0].y = corners[1].y;
344 corners[1].y = temp;
345 }
346
347 /* In GM_COMPATIBLE, don't include bottom and right edges */
348 if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE)
349 {
350 corners[1].x--;
351 corners[1].y--;
352 }
353
354 /* Close any previous figure */
355 IntGdiCloseFigure(pPath);
356
357 /* Add four points to the path */
358 pointTemp.x = corners[1].x;
359 pointTemp.y = corners[0].y;
360 if (!PATH_AddEntry(pPath, &pointTemp, PT_MOVETO))
361 {
362 PATH_UnlockPath(pPath);
363 return FALSE;
364 }
365 if (!PATH_AddEntry(pPath, corners, PT_LINETO))
366 {
367 PATH_UnlockPath(pPath);
368 return FALSE;
369 }
370 pointTemp.x = corners[0].x;
371 pointTemp.y = corners[1].y;
372 if (!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
373 {
374 PATH_UnlockPath(pPath);
375 return FALSE;
376 }
377 if (!PATH_AddEntry(pPath, corners + 1, PT_LINETO))
378 {
379 PATH_UnlockPath(pPath);
380 return FALSE;
381 }
382
383 /* Close the rectangle figure */
384 IntGdiCloseFigure(pPath) ;
385 PATH_UnlockPath(pPath);
386 return TRUE;
387 }
388
389 /* PATH_RoundRect
390 *
391 * Should be called when a call to RoundRect is performed on a DC that has
392 * an open path. Returns TRUE if successful, else FALSE.
393 *
394 * FIXME: It adds the same entries to the path as windows does, but there
395 * is an error in the bezier drawing code so that there are small pixel-size
396 * gaps when the resulting path is drawn by StrokePath()
397 */
398 BOOL
399 FASTCALL
400 PATH_RoundRect(
401 DC *dc,
402 INT x1,
403 INT y1,
404 INT x2,
405 INT y2,
406 INT ell_width,
407 INT ell_height)
408 {
409 PPATH pPath;
410 POINT corners[2], pointTemp;
411 FLOAT_POINT ellCorners[2];
412
413 pPath = PATH_LockPath(dc->dclevel.hPath);
414 if (!pPath) return FALSE;
415
416 /* Check that path is open */
417 if (pPath->state != PATH_Open)
418 {
419 PATH_UnlockPath(pPath);
420 return FALSE;
421 }
422
423 if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
424 {
425 PATH_UnlockPath(pPath);
426 return FALSE;
427 }
428
429 /* Add points to the roundrect path */
430 ellCorners[0].x = corners[1].x - ell_width;
431 ellCorners[0].y = corners[0].y;
432 ellCorners[1].x = corners[1].x;
433 ellCorners[1].y = corners[0].y + ell_height;
434 if (!PATH_DoArcPart(pPath, ellCorners, 0, -M_PI_2, PT_MOVETO))
435 {
436 PATH_UnlockPath(pPath);
437 return FALSE;
438 }
439 pointTemp.x = corners[0].x + ell_width / 2;
440 pointTemp.y = corners[0].y;
441 if (!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
442 {
443 PATH_UnlockPath(pPath);
444 return FALSE;
445 }
446 ellCorners[0].x = corners[0].x;
447 ellCorners[1].x = corners[0].x + ell_width;
448 if (!PATH_DoArcPart(pPath, ellCorners, -M_PI_2, -M_PI, FALSE))
449 {
450 PATH_UnlockPath(pPath);
451 return FALSE;
452 }
453 pointTemp.x = corners[0].x;
454 pointTemp.y = corners[1].y - ell_height / 2;
455 if (!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
456 {
457 PATH_UnlockPath(pPath);
458 return FALSE;
459 }
460 ellCorners[0].y = corners[1].y - ell_height;
461 ellCorners[1].y = corners[1].y;
462 if (!PATH_DoArcPart(pPath, ellCorners, M_PI, M_PI_2, FALSE))
463 {
464 PATH_UnlockPath(pPath);
465 return FALSE;
466 }
467 pointTemp.x = corners[1].x - ell_width / 2;
468 pointTemp.y = corners[1].y;
469 if (!PATH_AddEntry(pPath, &pointTemp, PT_LINETO))
470 {
471 PATH_UnlockPath(pPath);
472 return FALSE;
473 }
474 ellCorners[0].x = corners[1].x - ell_width;
475 ellCorners[1].x = corners[1].x;
476 if (!PATH_DoArcPart(pPath, ellCorners, M_PI_2, 0, FALSE))
477 {
478 PATH_UnlockPath(pPath);
479 return FALSE;
480 }
481
482 IntGdiCloseFigure(pPath);
483 PATH_UnlockPath(pPath);
484 return TRUE;
485 }
486
487 /* PATH_Ellipse
488 *
489 * Should be called when a call to Ellipse is performed on a DC that has
490 * an open path. This adds four Bezier splines representing the ellipse
491 * to the path. Returns TRUE if successful, else FALSE.
492 */
493 BOOL
494 FASTCALL
495 PATH_Ellipse(
496 PDC dc,
497 INT x1,
498 INT y1,
499 INT x2,
500 INT y2)
501 {
502 PPATH pPath;
503 /* TODO: This should probably be revised to call PATH_AngleArc */
504 /* (once it exists) */
505 BOOL Ret = PATH_Arc(dc, x1, y1, x2, y2, x1, (y1 + y2) / 2, x1, (y1 + y2) / 2, GdiTypeArc);
506 if (Ret)
507 {
508 pPath = PATH_LockPath(dc->dclevel.hPath);
509 if (!pPath) return FALSE;
510 IntGdiCloseFigure(pPath);
511 PATH_UnlockPath(pPath);
512 }
513 return Ret;
514 }
515
516 /* PATH_Arc
517 *
518 * Should be called when a call to Arc is performed on a DC that has
519 * an open path. This adds up to five Bezier splines representing the arc
520 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
521 * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
522 * -1 we add 1 extra line from the current DC position to the starting position
523 * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
524 * else FALSE.
525 */
526 BOOL
527 FASTCALL
528 PATH_Arc(
529 PDC dc,
530 INT x1,
531 INT y1,
532 INT x2,
533 INT y2,
534 INT xStart,
535 INT yStart,
536 INT xEnd,
537 INT yEnd,
538 INT lines)
539 {
540 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant = 0.0;
541 /* Initialize angleEndQuadrant to silence gcc's warning */
542 double x, y;
543 FLOAT_POINT corners[2], pointStart, pointEnd;
544 POINT centre, pointCurPos;
545 BOOL start, end, Ret = TRUE;
546 INT temp;
547 BOOL clockwise;
548 PPATH pPath;
549
550 /* FIXME: This function should check for all possible error returns */
551 /* FIXME: Do we have to respect newStroke? */
552
553 ASSERT(dc);
554
555 pPath = PATH_LockPath(dc->dclevel.hPath);
556 if (!pPath) return FALSE;
557
558 clockwise = ((dc->dclevel.flPath & DCPATH_CLOCKWISE) != 0);
559
560 /* Check that path is open */
561 if (pPath->state != PATH_Open)
562 {
563 Ret = FALSE;
564 goto ArcExit;
565 }
566
567 /* Check for zero height / width */
568 /* FIXME: Only in GM_COMPATIBLE? */
569 if (x1 == x2 || y1 == y2)
570 {
571 Ret = TRUE;
572 goto ArcExit;
573 }
574 /* Convert points to device coordinates */
575 corners[0].x = (FLOAT)x1;
576 corners[0].y = (FLOAT)y1;
577 corners[1].x = (FLOAT)x2;
578 corners[1].y = (FLOAT)y2;
579 pointStart.x = (FLOAT)xStart;
580 pointStart.y = (FLOAT)yStart;
581 pointEnd.x = (FLOAT)xEnd;
582 pointEnd.y = (FLOAT)yEnd;
583 INTERNAL_LPTODP_FLOAT(dc, corners);
584 INTERNAL_LPTODP_FLOAT(dc, corners + 1);
585 INTERNAL_LPTODP_FLOAT(dc, &pointStart);
586 INTERNAL_LPTODP_FLOAT(dc, &pointEnd);
587
588 /* Make sure first corner is top left and second corner is bottom right */
589 if (corners[0].x > corners[1].x)
590 {
591 temp = corners[0].x;
592 corners[0].x = corners[1].x;
593 corners[1].x = temp;
594 }
595 if (corners[0].y > corners[1].y)
596 {
597 temp = corners[0].y;
598 corners[0].y = corners[1].y;
599 corners[1].y = temp;
600 }
601
602 /* Compute start and end angle */
603 PATH_NormalizePoint(corners, &pointStart, &x, &y);
604 angleStart = atan2(y, x);
605 PATH_NormalizePoint(corners, &pointEnd, &x, &y);
606 angleEnd = atan2(y, x);
607
608 /* Make sure the end angle is "on the right side" of the start angle */
609 if (clockwise)
610 {
611 if (angleEnd <= angleStart)
612 {
613 angleEnd += 2 * M_PI;
614 ASSERT(angleEnd >= angleStart);
615 }
616 }
617 else
618 {
619 if (angleEnd >= angleStart)
620 {
621 angleEnd -= 2 * M_PI;
622 ASSERT(angleEnd <= angleStart);
623 }
624 }
625
626 /* In GM_COMPATIBLE, don't include bottom and right edges */
627 if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE)
628 {
629 corners[1].x--;
630 corners[1].y--;
631 }
632
633 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
634 if (lines == GdiTypeArcTo && pPath->newStroke) // -1
635 {
636 pPath->newStroke = FALSE;
637 IntGetCurrentPositionEx(dc, &pointCurPos);
638 CoordLPtoDP(dc, &pointCurPos);
639 if (!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
640 {
641 Ret = FALSE;
642 goto ArcExit;
643 }
644 }
645
646 /* Add the arc to the path with one Bezier spline per quadrant that the
647 * arc spans */
648 start = TRUE;
649 end = FALSE;
650 do
651 {
652 /* Determine the start and end angles for this quadrant */
653 if (start)
654 {
655 angleStartQuadrant = angleStart;
656 if (clockwise)
657 angleEndQuadrant = (floor(angleStart / M_PI_2) + 1.0) * M_PI_2;
658 else
659 angleEndQuadrant = (ceil(angleStart / M_PI_2) - 1.0) * M_PI_2;
660 }
661 else
662 {
663 angleStartQuadrant = angleEndQuadrant;
664 if (clockwise)
665 angleEndQuadrant += M_PI_2;
666 else
667 angleEndQuadrant -= M_PI_2;
668 }
669
670 /* Have we reached the last part of the arc? */
671 if ((clockwise && angleEnd < angleEndQuadrant) ||
672 (!clockwise && angleEnd > angleEndQuadrant))
673 {
674 /* Adjust the end angle for this quadrant */
675 angleEndQuadrant = angleEnd;
676 end = TRUE;
677 }
678
679 /* Add the Bezier spline to the path */
680 PATH_DoArcPart(pPath,
681 corners,
682 angleStartQuadrant,
683 angleEndQuadrant,
684 start ? (lines == GdiTypeArcTo ? PT_LINETO : PT_MOVETO) : FALSE); // -1
685 start = FALSE;
686 }
687 while (!end);
688
689 /* chord: close figure. pie: add line and close figure */
690 if (lines == GdiTypeChord) // 1
691 {
692 IntGdiCloseFigure(pPath);
693 }
694 else if (lines == GdiTypePie) // 2
695 {
696 centre.x = (corners[0].x + corners[1].x) / 2;
697 centre.y = (corners[0].y + corners[1].y) / 2;
698 if (!PATH_AddEntry(pPath, &centre, PT_LINETO | PT_CLOSEFIGURE))
699 Ret = FALSE;
700 }
701 ArcExit:
702 PATH_UnlockPath(pPath);
703 return Ret;
704 }
705
706 BOOL
707 FASTCALL
708 PATH_PolyBezierTo(
709 PDC dc,
710 const POINT *pts,
711 DWORD cbPoints)
712 {
713 POINT pt;
714 ULONG i;
715 PPATH pPath;
716
717 ASSERT(dc);
718 ASSERT(pts);
719 ASSERT(cbPoints);
720
721 pPath = PATH_LockPath(dc->dclevel.hPath);
722 if (!pPath) return FALSE;
723
724 /* Check that path is open */
725 if (pPath->state != PATH_Open)
726 {
727 PATH_UnlockPath(pPath);
728 return FALSE;
729 }
730
731 /* Add a PT_MOVETO if necessary */
732 if (pPath->newStroke)
733 {
734 pPath->newStroke = FALSE;
735 IntGetCurrentPositionEx(dc, &pt);
736 CoordLPtoDP(dc, &pt);
737 if (!PATH_AddEntry(pPath, &pt, PT_MOVETO))
738 {
739 PATH_UnlockPath(pPath);
740 return FALSE;
741 }
742 }
743
744 for (i = 0; i < cbPoints; i++)
745 {
746 pt = pts[i];
747 CoordLPtoDP(dc, &pt);
748 PATH_AddEntry(pPath, &pt, PT_BEZIERTO);
749 }
750
751 PATH_UnlockPath(pPath);
752 return TRUE;
753 }
754
755 BOOL
756 FASTCALL
757 PATH_PolyBezier(
758 PDC dc,
759 const POINT *pts,
760 DWORD cbPoints)
761 {
762 POINT pt;
763 ULONG i;
764 PPATH pPath;
765
766 ASSERT(dc);
767 ASSERT(pts);
768 ASSERT(cbPoints);
769
770 pPath = PATH_LockPath(dc->dclevel.hPath);
771 if (!pPath) return FALSE;
772
773 /* Check that path is open */
774 if (pPath->state != PATH_Open)
775 {
776 PATH_UnlockPath(pPath);
777 return FALSE;
778 }
779
780 for (i = 0; i < cbPoints; i++)
781 {
782 pt = pts[i];
783 CoordLPtoDP(dc, &pt);
784 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_BEZIERTO);
785 }
786
787 PATH_UnlockPath(pPath);
788 return TRUE;
789 }
790
791 BOOL
792 FASTCALL
793 PATH_PolyDraw(
794 PDC dc,
795 const POINT *pts,
796 const BYTE *types,
797 DWORD cbPoints)
798 {
799 PPATH pPath;
800 POINT lastmove, orig_pos;
801 ULONG i;
802 PDC_ATTR pdcattr;
803 BOOL State = FALSE, Ret = FALSE;
804
805 pPath = PATH_LockPath(dc->dclevel.hPath);
806 if (!pPath) return FALSE;
807
808 if (pPath->state != PATH_Open)
809 {
810 PATH_UnlockPath(pPath);
811 return FALSE;
812 }
813
814 pdcattr = dc->pdcattr;
815
816 lastmove.x = orig_pos.x = pdcattr->ptlCurrent.x;
817 lastmove.y = orig_pos.y = pdcattr->ptlCurrent.y;
818
819 i = pPath->numEntriesUsed;
820
821 while (i != 0)
822 {
823 i--;
824 if (pPath->pFlags[i] == PT_MOVETO)
825 {
826 lastmove.x = pPath->pPoints[i].x;
827 lastmove.y = pPath->pPoints[i].y;
828 if (!GdiPathDPtoLP(dc, &lastmove, 1))
829 {
830 PATH_UnlockPath(pPath);
831 return FALSE;
832 }
833 break;
834 }
835 }
836
837 for (i = 0; i < cbPoints; i++)
838 {
839 if (types[i] == PT_MOVETO)
840 {
841 pPath->newStroke = TRUE;
842 lastmove.x = pts[i].x;
843 lastmove.y = pts[i].y;
844 }
845 else if ((types[i] & ~PT_CLOSEFIGURE) == PT_LINETO)
846 {
847 PATH_LineTo(dc, pts[i].x, pts[i].y);
848 }
849 else if (types[i] == PT_BEZIERTO)
850 {
851 if (!((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO)
852 && ((types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)))
853 goto err;
854 PATH_PolyBezierTo(dc, &(pts[i]), 3);
855 i += 2;
856 }
857 else
858 goto err;
859
860 pdcattr->ptlCurrent.x = pts[i].x;
861 pdcattr->ptlCurrent.y = pts[i].y;
862 State = TRUE;
863
864 if (types[i] & PT_CLOSEFIGURE)
865 {
866 pPath->pFlags[pPath->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
867 pPath->newStroke = TRUE;
868 pdcattr->ptlCurrent.x = lastmove.x;
869 pdcattr->ptlCurrent.y = lastmove.y;
870 State = TRUE;
871 }
872 }
873 Ret = TRUE;
874 goto Exit;
875
876 err:
877 if ((pdcattr->ptlCurrent.x != orig_pos.x) || (pdcattr->ptlCurrent.y != orig_pos.y))
878 {
879 pPath->newStroke = TRUE;
880 pdcattr->ptlCurrent.x = orig_pos.x;
881 pdcattr->ptlCurrent.y = orig_pos.y;
882 State = TRUE;
883 }
884 Exit:
885 if (State) // State change?
886 {
887 pdcattr->ptfxCurrent = pdcattr->ptlCurrent;
888 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx
889 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT | DIRTY_PTFXCURRENT | DIRTY_STYLESTATE);
890 }
891 PATH_UnlockPath(pPath);
892 return Ret;
893 }
894
895 BOOL
896 FASTCALL
897 PATH_Polyline(
898 PDC dc,
899 const POINT *pts,
900 DWORD cbPoints)
901 {
902 POINT pt;
903 ULONG i;
904 PPATH pPath;
905
906 ASSERT(dc);
907 ASSERT(pts);
908 ASSERT(cbPoints);
909
910 pPath = PATH_LockPath(dc->dclevel.hPath);
911 if (!pPath) return FALSE;
912
913 /* Check that path is open */
914 if (pPath->state != PATH_Open)
915 {
916 PATH_UnlockPath(pPath);
917 return FALSE;
918 }
919 for (i = 0; i < cbPoints; i++)
920 {
921 pt = pts[i];
922 CoordLPtoDP(dc, &pt);
923 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO : PT_LINETO);
924 }
925 PATH_UnlockPath(pPath);
926 return TRUE;
927 }
928
929 BOOL
930 FASTCALL
931 PATH_PolylineTo(
932 PDC dc,
933 const POINT *pts,
934 DWORD cbPoints)
935 {
936 POINT pt;
937 ULONG i;
938 PPATH pPath;
939
940 ASSERT(dc);
941 ASSERT(pts);
942 ASSERT(cbPoints);
943
944 pPath = PATH_LockPath(dc->dclevel.hPath);
945 if (!pPath) return FALSE;
946
947 /* Check that path is open */
948 if (pPath->state != PATH_Open)
949 {
950 PATH_UnlockPath(pPath);
951 return FALSE;
952 }
953
954 /* Add a PT_MOVETO if necessary */
955 if (pPath->newStroke)
956 {
957 pPath->newStroke = FALSE;
958 IntGetCurrentPositionEx(dc, &pt);
959 CoordLPtoDP(dc, &pt);
960 if (!PATH_AddEntry(pPath, &pt, PT_MOVETO))
961 {
962 PATH_UnlockPath(pPath);
963 return FALSE;
964 }
965 }
966
967 for (i = 0; i < cbPoints; i++)
968 {
969 pt = pts[i];
970 CoordLPtoDP(dc, &pt);
971 PATH_AddEntry(pPath, &pt, PT_LINETO);
972 }
973 PATH_UnlockPath(pPath);
974 return TRUE;
975 }
976
977
978 BOOL
979 FASTCALL
980 PATH_Polygon(
981 PDC dc,
982 const POINT *pts,
983 DWORD cbPoints)
984 {
985 POINT pt;
986 ULONG i;
987 PPATH pPath;
988
989 ASSERT(dc);
990 ASSERT(pts);
991
992 pPath = PATH_LockPath(dc->dclevel.hPath);
993 if (!pPath) return FALSE;
994
995 /* Check that path is open */
996 if (pPath->state != PATH_Open)
997 {
998 PATH_UnlockPath(pPath);
999 return FALSE;
1000 }
1001
1002 for (i = 0; i < cbPoints; i++)
1003 {
1004 pt = pts[i];
1005 CoordLPtoDP(dc, &pt);
1006 PATH_AddEntry(pPath, &pt, (i == 0) ? PT_MOVETO :
1007 ((i == cbPoints - 1) ? PT_LINETO | PT_CLOSEFIGURE :
1008 PT_LINETO));
1009 }
1010
1011 PATH_UnlockPath(pPath);
1012 return TRUE;
1013 }
1014
1015 BOOL
1016 FASTCALL
1017 PATH_PolyPolygon(
1018 PDC dc,
1019 const POINT* pts,
1020 const INT* counts,
1021 UINT polygons)
1022 {
1023 POINT pt, startpt;
1024 ULONG poly, point, i;
1025 PPATH pPath;
1026
1027 ASSERT(dc);
1028 ASSERT(pts);
1029 ASSERT(counts);
1030 ASSERT(polygons);
1031
1032 pPath = PATH_LockPath(dc->dclevel.hPath);
1033 if (!pPath) return FALSE;
1034
1035 /* Check that path is open */
1036 if (pPath->state != PATH_Open)
1037 {
1038 PATH_UnlockPath(pPath);
1039 return FALSE;
1040 }
1041
1042 for (i = 0, poly = 0; poly < polygons; poly++)
1043 {
1044 for (point = 0; point < (ULONG) counts[poly]; point++, i++)
1045 {
1046 pt = pts[i];
1047 CoordLPtoDP(dc, &pt);
1048 if (point == 0) startpt = pt;
1049 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1050 }
1051
1052 /* Win98 adds an extra line to close the figure for some reason */
1053 PATH_AddEntry(pPath, &startpt, PT_LINETO | PT_CLOSEFIGURE);
1054 }
1055
1056 PATH_UnlockPath(pPath);
1057 return TRUE;
1058 }
1059
1060 BOOL
1061 FASTCALL
1062 PATH_PolyPolyline(
1063 PDC dc,
1064 const POINT* pts,
1065 const DWORD* counts,
1066 DWORD polylines)
1067 {
1068 POINT pt;
1069 ULONG poly, point, i;
1070 PPATH pPath;
1071
1072 ASSERT(dc);
1073 ASSERT(pts);
1074 ASSERT(counts);
1075 ASSERT(polylines);
1076
1077 pPath = PATH_LockPath(dc->dclevel.hPath);
1078 if (!pPath) return FALSE;
1079
1080 /* Check that path is open */
1081 if (pPath->state != PATH_Open)
1082 {
1083 PATH_UnlockPath(pPath);
1084 return FALSE;
1085 }
1086
1087 for (i = 0, poly = 0; poly < polylines; poly++)
1088 {
1089 for (point = 0; point < counts[poly]; point++, i++)
1090 {
1091 pt = pts[i];
1092 CoordLPtoDP(dc, &pt);
1093 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1094 }
1095 }
1096
1097 PATH_UnlockPath(pPath);
1098 return TRUE;
1099 }
1100
1101
1102 /* PATH_CheckCorners
1103 *
1104 * Helper function for PATH_RoundRect() and PATH_Rectangle()
1105 */
1106 BOOL
1107 PATH_CheckCorners(
1108 DC *dc,
1109 POINT corners[],
1110 INT x1,
1111 INT y1,
1112 INT x2,
1113 INT y2)
1114 {
1115 INT temp;
1116 PDC_ATTR pdcattr = dc->pdcattr;
1117
1118 /* Convert points to device coordinates */
1119 corners[0].x = x1;
1120 corners[0].y = y1;
1121 corners[1].x = x2;
1122 corners[1].y = y2;
1123 CoordLPtoDP(dc, &corners[0]);
1124 CoordLPtoDP(dc, &corners[1]);
1125
1126 /* Make sure first corner is top left and second corner is bottom right */
1127 if (corners[0].x > corners[1].x)
1128 {
1129 temp = corners[0].x;
1130 corners[0].x = corners[1].x;
1131 corners[1].x = temp;
1132 }
1133
1134 if (corners[0].y > corners[1].y)
1135 {
1136 temp = corners[0].y;
1137 corners[0].y = corners[1].y;
1138 corners[1].y = temp;
1139 }
1140
1141 /* In GM_COMPATIBLE, don't include bottom and right edges */
1142 if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
1143 {
1144 corners[1].x--;
1145 corners[1].y--;
1146 }
1147
1148 return TRUE;
1149 }
1150
1151
1152 /* PATH_AddFlatBezier
1153 *
1154 */
1155 BOOL
1156 FASTCALL
1157 PATH_AddFlatBezier(
1158 PPATH pPath,
1159 POINT *pt,
1160 BOOL closed)
1161 {
1162 POINT *pts;
1163 INT no, i;
1164
1165 pts = GDI_Bezier(pt, 4, &no);
1166 if (!pts) return FALSE;
1167
1168 for (i = 1; i < no; i++)
1169 PATH_AddEntry(pPath, &pts[i], (i == no - 1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO);
1170
1171 ExFreePoolWithTag(pts, TAG_BEZIER);
1172 return TRUE;
1173 }
1174
1175 /* PATH_FlattenPath
1176 *
1177 * Replaces Beziers with line segments
1178 *
1179 */
1180 BOOL
1181 FASTCALL
1182 PATH_FlattenPath(PPATH pPath)
1183 {
1184 PATH newPath;
1185 INT srcpt;
1186
1187 RtlZeroMemory(&newPath, sizeof(newPath));
1188 newPath.state = PATH_Open;
1189 for (srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++)
1190 {
1191 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE)
1192 {
1193 case PT_MOVETO:
1194 case PT_LINETO:
1195 PATH_AddEntry(&newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]);
1196 break;
1197 case PT_BEZIERTO:
1198 PATH_AddFlatBezier(&newPath, &pPath->pPoints[srcpt - 1], pPath->pFlags[srcpt + 2] & PT_CLOSEFIGURE);
1199 srcpt += 2;
1200 break;
1201 }
1202 }
1203
1204 newPath.state = PATH_Closed;
1205 PATH_AssignGdiPath(pPath, &newPath);
1206 PATH_EmptyPath(&newPath);
1207 return TRUE;
1208 }
1209
1210
1211 /* PATH_PathToRegion
1212 *
1213 * Creates a region from the specified path using the specified polygon
1214 * filling mode. The path is left unchanged. A handle to the region that
1215 * was created is stored in *pHrgn. If successful, TRUE is returned; if an
1216 * error occurs, SetLastError is called with the appropriate value and
1217 * FALSE is returned.
1218 */
1219 BOOL
1220 FASTCALL
1221 PATH_PathToRegion(
1222 PPATH pPath,
1223 INT nPolyFillMode,
1224 HRGN *pHrgn)
1225 {
1226 int numStrokes, iStroke, i;
1227 PULONG pNumPointsInStroke;
1228 HRGN hrgn = 0;
1229
1230 ASSERT(pPath != NULL);
1231 ASSERT(pHrgn != NULL);
1232
1233 PATH_FlattenPath(pPath);
1234
1235 /* First pass: Find out how many strokes there are in the path */
1236 /* FIXME: We could eliminate this with some bookkeeping in GdiPath */
1237 numStrokes = 0;
1238 for (i = 0; i < pPath->numEntriesUsed; i++)
1239 if ((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1240 numStrokes++;
1241
1242 if (numStrokes == 0)
1243 {
1244 return FALSE;
1245 }
1246
1247 /* Allocate memory for number-of-points-in-stroke array */
1248 pNumPointsInStroke = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * numStrokes, TAG_PATH);
1249 if (!pNumPointsInStroke)
1250 {
1251 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1252 return FALSE;
1253 }
1254
1255 /* Second pass: remember number of points in each polygon */
1256 iStroke = -1; /* Will get incremented to 0 at beginning of first stroke */
1257 for (i = 0; i < pPath->numEntriesUsed; i++)
1258 {
1259 /* Is this the beginning of a new stroke? */
1260 if ((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1261 {
1262 iStroke++;
1263 _PRAGMA_WARNING_SUPPRESS(__WARNING_WRITE_OVERRUN)
1264 pNumPointsInStroke[iStroke] = 0;
1265 }
1266
1267 _PRAGMA_WARNING_SUPPRESS(__WARNING_READ_OVERRUN)
1268 pNumPointsInStroke[iStroke]++;
1269 }
1270
1271 /* Create a region from the strokes */
1272 hrgn = IntCreatePolyPolygonRgn(pPath->pPoints,
1273 pNumPointsInStroke,
1274 numStrokes,
1275 nPolyFillMode);
1276 if (hrgn == (HRGN)0)
1277 {
1278 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1279 return FALSE;
1280 }
1281
1282 /* Free memory for number-of-points-in-stroke array */
1283 ExFreePoolWithTag(pNumPointsInStroke, TAG_PATH);
1284
1285 /* Success! */
1286 *pHrgn = hrgn;
1287 return TRUE;
1288 }
1289
1290 /* PATH_EmptyPath
1291 *
1292 * Removes all entries from the path and sets the path state to PATH_Null.
1293 */
1294 VOID
1295 FASTCALL
1296 PATH_EmptyPath(PPATH pPath)
1297 {
1298 ASSERT(pPath != NULL);
1299
1300 pPath->state = PATH_Null;
1301 pPath->numEntriesUsed = 0;
1302 }
1303
1304 /* PATH_AddEntry
1305 *
1306 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1307 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1308 * successful, FALSE otherwise (e.g. if not enough memory was available).
1309 */
1310 BOOL
1311 FASTCALL
1312 PATH_AddEntry(
1313 PPATH pPath,
1314 const POINT *pPoint,
1315 BYTE flags)
1316 {
1317 ASSERT(pPath != NULL);
1318
1319 /* FIXME: If newStroke is true, perhaps we want to check that we're
1320 * getting a PT_MOVETO
1321 */
1322
1323 /* Check that path is open */
1324 if (pPath->state != PATH_Open)
1325 return FALSE;
1326
1327 /* Reserve enough memory for an extra path entry */
1328 if (!PATH_ReserveEntries(pPath, pPath->numEntriesUsed + 1))
1329 return FALSE;
1330
1331 /* Store information in path entry */
1332 pPath->pPoints[pPath->numEntriesUsed] = *pPoint;
1333 pPath->pFlags[pPath->numEntriesUsed] = flags;
1334
1335 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1336 if ((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1337 pPath->newStroke = TRUE;
1338
1339 /* Increment entry count */
1340 pPath->numEntriesUsed++;
1341
1342 return TRUE;
1343 }
1344
1345 /* PATH_ReserveEntries
1346 *
1347 * Ensures that at least "numEntries" entries (for points and flags) have
1348 * been allocated; allocates larger arrays and copies the existing entries
1349 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1350 */
1351 BOOL
1352 FASTCALL
1353 PATH_ReserveEntries(
1354 PPATH pPath,
1355 INT numEntries)
1356 {
1357 INT numEntriesToAllocate;
1358 POINT *pPointsNew;
1359 BYTE *pFlagsNew;
1360
1361 ASSERT(pPath != NULL);
1362 ASSERT(numEntries >= 0);
1363
1364 /* Do we have to allocate more memory? */
1365 if (numEntries > pPath->numEntriesAllocated)
1366 {
1367 /* Find number of entries to allocate. We let the size of the array
1368 * grow exponentially, since that will guarantee linear time
1369 * complexity. */
1370 if (pPath->numEntriesAllocated)
1371 {
1372 numEntriesToAllocate = pPath->numEntriesAllocated;
1373 while (numEntriesToAllocate < numEntries)
1374 numEntriesToAllocate = numEntriesToAllocate * GROW_FACTOR_NUMER / GROW_FACTOR_DENOM;
1375 }
1376 else
1377 numEntriesToAllocate = numEntries;
1378
1379 /* Allocate new arrays */
1380 pPointsNew = (POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
1381 if (!pPointsNew)
1382 return FALSE;
1383
1384 pFlagsNew = (BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
1385 if (!pFlagsNew)
1386 {
1387 ExFreePoolWithTag(pPointsNew, TAG_PATH);
1388 return FALSE;
1389 }
1390
1391 /* Copy old arrays to new arrays and discard old arrays */
1392 if (pPath->pPoints)
1393 {
1394 ASSERT(pPath->pFlags);
1395
1396 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
1397 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
1398
1399 ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
1400 ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
1401 }
1402
1403 pPath->pPoints = pPointsNew;
1404 pPath->pFlags = pFlagsNew;
1405 pPath->numEntriesAllocated = numEntriesToAllocate;
1406 }
1407
1408 return TRUE;
1409 }
1410
1411 /* PATH_DoArcPart
1412 *
1413 * Creates a Bezier spline that corresponds to part of an arc and appends the
1414 * corresponding points to the path. The start and end angles are passed in
1415 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1416 * at most. If "startEntryType" is non-zero, an entry of that type for the first
1417 * control point is added to the path; otherwise, it is assumed that the current
1418 * position is equal to the first control point.
1419 */
1420 BOOL
1421 FASTCALL
1422 PATH_DoArcPart(
1423 PPATH pPath,
1424 FLOAT_POINT corners[],
1425 double angleStart,
1426 double angleEnd,
1427 BYTE startEntryType)
1428 {
1429 double halfAngle, a;
1430 double xNorm[4], yNorm[4];
1431 POINT point;
1432 int i;
1433
1434 ASSERT(fabs(angleEnd - angleStart) <= M_PI_2);
1435
1436 /* FIXME: Is there an easier way of computing this? */
1437
1438 /* Compute control points */
1439 halfAngle = (angleEnd - angleStart) / 2.0;
1440 if (fabs(halfAngle) > 1e-8)
1441 {
1442 a = 4.0 / 3.0 * (1 - cos(halfAngle)) / sin(halfAngle);
1443 xNorm[0] = cos(angleStart);
1444 yNorm[0] = sin(angleStart);
1445 xNorm[1] = xNorm[0] - a * yNorm[0];
1446 yNorm[1] = yNorm[0] + a * xNorm[0];
1447 xNorm[3] = cos(angleEnd);
1448 yNorm[3] = sin(angleEnd);
1449 xNorm[2] = xNorm[3] + a * yNorm[3];
1450 yNorm[2] = yNorm[3] - a * xNorm[3];
1451 }
1452 else
1453 for (i = 0; i < 4; i++)
1454 {
1455 xNorm[i] = cos(angleStart);
1456 yNorm[i] = sin(angleStart);
1457 }
1458
1459 /* Add starting point to path if desired */
1460 if (startEntryType)
1461 {
1462 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1463 if (!PATH_AddEntry(pPath, &point, startEntryType))
1464 return FALSE;
1465 }
1466
1467 /* Add remaining control points */
1468 for (i = 1; i < 4; i++)
1469 {
1470 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1471 if (!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1472 return FALSE;
1473 }
1474
1475 return TRUE;
1476 }
1477
1478 /* PATH_ScaleNormalizedPoint
1479 *
1480 * Scales a normalized point (x, y) with respect to the box whose corners are
1481 * passed in "corners". The point is stored in "*pPoint". The normalized
1482 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1483 * (1.0, 1.0) correspond to corners[1].
1484 */
1485 VOID
1486 FASTCALL
1487 PATH_ScaleNormalizedPoint(
1488 FLOAT_POINT corners[],
1489 double x,
1490 double y,
1491 POINT *pPoint)
1492 {
1493 ASSERT(corners);
1494 ASSERT(pPoint);
1495
1496 pPoint->x = GDI_ROUND((double)corners[0].x + (double)(corners[1].x - corners[0].x) * 0.5 * (x + 1.0));
1497 pPoint->y = GDI_ROUND((double)corners[0].y + (double)(corners[1].y - corners[0].y) * 0.5 * (y + 1.0));
1498 }
1499
1500 /* PATH_NormalizePoint
1501 *
1502 * Normalizes a point with respect to the box whose corners are passed in
1503 * corners. The normalized coordinates are stored in *pX and *pY.
1504 */
1505 VOID
1506 FASTCALL
1507 PATH_NormalizePoint(
1508 FLOAT_POINT corners[],
1509 const FLOAT_POINT *pPoint,
1510 double *pX,
1511 double *pY)
1512 {
1513 ASSERT(corners);
1514 ASSERT(pPoint);
1515 ASSERT(pX);
1516 ASSERT(pY);
1517
1518 *pX = (double)(pPoint->x - corners[0].x) / (double)(corners[1].x - corners[0].x) * 2.0 - 1.0;
1519 *pY = (double)(pPoint->y - corners[0].y) / (double)(corners[1].y - corners[0].y) * 2.0 - 1.0;
1520 }
1521
1522
1523 BOOL
1524 FASTCALL
1525 PATH_StrokePath(
1526 DC *dc,
1527 PPATH pPath)
1528 {
1529 BOOL ret = FALSE;
1530 INT i = 0;
1531 INT nLinePts, nAlloc;
1532 POINT *pLinePts = NULL;
1533 POINT ptViewportOrg, ptWindowOrg;
1534 SIZE szViewportExt, szWindowExt;
1535 DWORD mapMode, graphicsMode;
1536 XFORM xform;
1537 PDC_ATTR pdcattr = dc->pdcattr;
1538
1539 DPRINT("Enter %s\n", __FUNCTION__);
1540
1541 if (pPath->state != PATH_Closed)
1542 return FALSE;
1543
1544 /* Save the mapping mode info */
1545 mapMode = pdcattr->iMapMode;
1546
1547 szViewportExt = *DC_pszlViewportExt(dc);
1548 ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1549 szWindowExt = dc->pdcattr->szlWindowExt;
1550 ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1551
1552 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1553
1554 /* Set MM_TEXT */
1555 pdcattr->iMapMode = MM_TEXT;
1556 pdcattr->ptlViewportOrg.x = 0;
1557 pdcattr->ptlViewportOrg.y = 0;
1558 pdcattr->ptlWindowOrg.x = 0;
1559 pdcattr->ptlWindowOrg.y = 0;
1560 graphicsMode = pdcattr->iGraphicsMode;
1561 pdcattr->iGraphicsMode = GM_ADVANCED;
1562 GreModifyWorldTransform(dc, (XFORML*)&xform, MWT_IDENTITY);
1563 pdcattr->iGraphicsMode = graphicsMode;
1564
1565 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1566 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1567 * space in case we get one to keep the number of reallocations small. */
1568 nAlloc = pPath->numEntriesUsed + 1 + 300;
1569 pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1570 if (!pLinePts)
1571 {
1572 DPRINT1("Can't allocate pool!\n");
1573 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1574 goto end;
1575 }
1576 nLinePts = 0;
1577
1578 for (i = 0; i < pPath->numEntriesUsed; i++)
1579 {
1580 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1581 && (pPath->pFlags[i] != PT_MOVETO))
1582 {
1583 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1584 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1585 (INT)pPath->pFlags[i]);
1586 goto end;
1587 }
1588
1589 switch(pPath->pFlags[i])
1590 {
1591 case PT_MOVETO:
1592 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1593 pPath->pPoints[i].x, pPath->pPoints[i].y);
1594 if (nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1595 nLinePts = 0;
1596 pLinePts[nLinePts++] = pPath->pPoints[i];
1597 break;
1598 case PT_LINETO:
1599 case (PT_LINETO | PT_CLOSEFIGURE):
1600 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1601 pPath->pPoints[i].x, pPath->pPoints[i].y);
1602 pLinePts[nLinePts++] = pPath->pPoints[i];
1603 break;
1604 case PT_BEZIERTO:
1605 DPRINT("Got PT_BEZIERTO\n");
1606 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1607 (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1608 {
1609 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1610 ret = FALSE;
1611 goto end;
1612 }
1613 else
1614 {
1615 INT nBzrPts, nMinAlloc;
1616 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i - 1], 4, &nBzrPts);
1617 /* Make sure we have allocated enough memory for the lines of
1618 * this bezier and the rest of the path, assuming we won't get
1619 * another one (since we won't reallocate again then). */
1620 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1621 if (nAlloc < nMinAlloc)
1622 {
1623 // Reallocate memory
1624
1625 POINT *Realloc = NULL;
1626 nAlloc = nMinAlloc * 2;
1627
1628 Realloc = ExAllocatePoolWithTag(PagedPool,
1629 nAlloc * sizeof(POINT),
1630 TAG_PATH);
1631
1632 if (!Realloc)
1633 {
1634 DPRINT1("Can't allocate pool!\n");
1635 goto end;
1636 }
1637
1638 memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1639 ExFreePoolWithTag(pLinePts, TAG_PATH);
1640 pLinePts = Realloc;
1641 }
1642 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1643 nLinePts += nBzrPts - 1;
1644 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1645 i += 2;
1646 }
1647 break;
1648 default:
1649 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1650 goto end;
1651 }
1652
1653 if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1654 {
1655 pLinePts[nLinePts++] = pLinePts[0];
1656 }
1657 }
1658 if (nLinePts >= 2)
1659 IntGdiPolyline(dc, pLinePts, nLinePts);
1660
1661 ret = TRUE;
1662
1663 end:
1664 if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1665
1666 /* Restore the old mapping mode */
1667 pdcattr->iMapMode = mapMode;
1668 pdcattr->szlWindowExt.cx = szWindowExt.cx;
1669 pdcattr->szlWindowExt.cy = szWindowExt.cy;
1670 pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1671 pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1672
1673 pdcattr->szlViewportExt.cx = szViewportExt.cx;
1674 pdcattr->szlViewportExt.cy = szViewportExt.cy;
1675 pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1676 pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1677
1678 /* Restore the world transform */
1679 XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1680
1681 /* If we've moved the current point then get its new position
1682 which will be in device (MM_TEXT) co-ords, convert it to
1683 logical co-ords and re-set it. This basically updates
1684 dc->CurPosX|Y so that their values are in the correct mapping
1685 mode.
1686 */
1687 if (i > 0)
1688 {
1689 POINT pt;
1690 IntGetCurrentPositionEx(dc, &pt);
1691 IntDPtoLP(dc, &pt, 1);
1692 IntGdiMoveToEx(dc, pt.x, pt.y, NULL, FALSE);
1693 }
1694 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1695 return ret;
1696 }
1697
1698 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1699
1700 static
1701 BOOL
1702 FASTCALL
1703 PATH_WidenPath(DC *dc)
1704 {
1705 INT i, j, numStrokes, numOldStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1706 BOOL ret = FALSE;
1707 PPATH pPath, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1708 EXTLOGPEN *elp;
1709 DWORD obj_type, joint, endcap, penType;
1710 PDC_ATTR pdcattr = dc->pdcattr;
1711
1712 pPath = PATH_LockPath(dc->dclevel.hPath);
1713 if (!pPath) return FALSE;
1714
1715 if (pPath->state == PATH_Open)
1716 {
1717 PATH_UnlockPath(pPath);
1718 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1719 return FALSE;
1720 }
1721
1722 PATH_FlattenPath(pPath);
1723
1724 size = GreGetObject(pdcattr->hpen, 0, NULL);
1725 if (!size)
1726 {
1727 PATH_UnlockPath(pPath);
1728 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1729 return FALSE;
1730 }
1731
1732 elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
1733 GreGetObject(pdcattr->hpen, size, elp);
1734
1735 obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
1736 if (obj_type == GDI_OBJECT_TYPE_PEN)
1737 {
1738 penStyle = ((LOGPEN*)elp)->lopnStyle;
1739 }
1740 else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
1741 {
1742 penStyle = elp->elpPenStyle;
1743 }
1744 else
1745 {
1746 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1747 ExFreePoolWithTag(elp, TAG_PATH);
1748 PATH_UnlockPath(pPath);
1749 return FALSE;
1750 }
1751
1752 penWidth = elp->elpWidth;
1753 ExFreePoolWithTag(elp, TAG_PATH);
1754
1755 endcap = (PS_ENDCAP_MASK & penStyle);
1756 joint = (PS_JOIN_MASK & penStyle);
1757 penType = (PS_TYPE_MASK & penStyle);
1758
1759 /* The function cannot apply to cosmetic pens */
1760 if (obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
1761 {
1762 PATH_UnlockPath(pPath);
1763 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1764 return FALSE;
1765 }
1766
1767 penWidthIn = penWidth / 2;
1768 penWidthOut = penWidth / 2;
1769 if (penWidthIn + penWidthOut < penWidth)
1770 penWidthOut++;
1771
1772 numStrokes = 0;
1773
1774 for (i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++)
1775 {
1776 POINT point;
1777 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1778 (pPath->pFlags[i] != PT_MOVETO))
1779 {
1780 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1781 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1782 pPath->pFlags[i]);
1783 return FALSE;
1784 }
1785 switch(pPath->pFlags[i])
1786 {
1787 case PT_MOVETO:
1788 if (numStrokes > 0)
1789 {
1790 pStrokes[numStrokes - 1]->state = PATH_Closed;
1791 }
1792 numOldStrokes = numStrokes;
1793 numStrokes++;
1794 j = 0;
1795 if (numStrokes == 1)
1796 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1797 else
1798 {
1799 pOldStrokes = pStrokes; // Save old pointer.
1800 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1801 if (!pStrokes) return FALSE;
1802 RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1803 ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1804 }
1805 if (!pStrokes) return FALSE;
1806 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1807 if (!pStrokes[numStrokes - 1])
1808 {
1809 ASSERT(FALSE); // FIXME
1810 }
1811
1812 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1813 pStrokes[numStrokes - 1]->state = PATH_Open;
1814 case PT_LINETO:
1815 case (PT_LINETO | PT_CLOSEFIGURE):
1816 point.x = pPath->pPoints[i].x;
1817 point.y = pPath->pPoints[i].y;
1818 PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
1819 break;
1820 case PT_BEZIERTO:
1821 /* Should never happen because of the FlattenPath call */
1822 DPRINT1("Should never happen\n");
1823 break;
1824 default:
1825 DPRINT1("Got path flag %c\n", pPath->pFlags[i]);
1826 return FALSE;
1827 }
1828 }
1829
1830 pNewPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1831 if (!pNewPath)
1832 {
1833 ASSERT(FALSE); // FIXME
1834 }
1835 PATH_InitGdiPath(pNewPath);
1836 pNewPath->state = PATH_Open;
1837
1838 for (i = 0; i < numStrokes; i++)
1839 {
1840 pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1841 PATH_InitGdiPath(pUpPath);
1842 pUpPath->state = PATH_Open;
1843 pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1844 PATH_InitGdiPath(pDownPath);
1845 pDownPath->state = PATH_Open;
1846
1847 for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1848 {
1849 /* Beginning or end of the path if not closed */
1850 if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1851 {
1852 /* Compute segment angle */
1853 double xo, yo, xa, ya, theta;
1854 POINT pt;
1855 FLOAT_POINT corners[2];
1856 if (j == 0)
1857 {
1858 xo = pStrokes[i]->pPoints[j].x;
1859 yo = pStrokes[i]->pPoints[j].y;
1860 xa = pStrokes[i]->pPoints[1].x;
1861 ya = pStrokes[i]->pPoints[1].y;
1862 }
1863 else
1864 {
1865 xa = pStrokes[i]->pPoints[j - 1].x;
1866 ya = pStrokes[i]->pPoints[j - 1].y;
1867 xo = pStrokes[i]->pPoints[j].x;
1868 yo = pStrokes[i]->pPoints[j].y;
1869 }
1870 theta = atan2(ya - yo, xa - xo);
1871 switch(endcap)
1872 {
1873 case PS_ENDCAP_SQUARE :
1874 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1875 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1876 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1877 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1878 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1879 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1880 break;
1881 case PS_ENDCAP_FLAT :
1882 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1883 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1884 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1885 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1886 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1887 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1888 break;
1889 case PS_ENDCAP_ROUND :
1890 default :
1891 corners[0].x = xo - penWidthIn;
1892 corners[0].y = yo - penWidthIn;
1893 corners[1].x = xo + penWidthOut;
1894 corners[1].y = yo + penWidthOut;
1895 PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1896 PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1897 PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1898 PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1899 break;
1900 }
1901 }
1902 /* Corpse of the path */
1903 else
1904 {
1905 /* Compute angle */
1906 INT previous, next;
1907 double xa, ya, xb, yb, xo, yo;
1908 double alpha, theta, miterWidth;
1909 DWORD _joint = joint;
1910 POINT pt;
1911 PPATH pInsidePath, pOutsidePath;
1912 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1913 {
1914 previous = j - 1;
1915 next = j + 1;
1916 }
1917 else if (j == 0)
1918 {
1919 previous = pStrokes[i]->numEntriesUsed - 1;
1920 next = j + 1;
1921 }
1922 else
1923 {
1924 previous = j - 1;
1925 next = 0;
1926 }
1927 xo = pStrokes[i]->pPoints[j].x;
1928 yo = pStrokes[i]->pPoints[j].y;
1929 xa = pStrokes[i]->pPoints[previous].x;
1930 ya = pStrokes[i]->pPoints[previous].y;
1931 xb = pStrokes[i]->pPoints[next].x;
1932 yb = pStrokes[i]->pPoints[next].y;
1933 theta = atan2(yo - ya, xo - xa);
1934 alpha = atan2(yb - yo, xb - xo) - theta;
1935 if (alpha > 0) alpha -= M_PI;
1936 else alpha += M_PI;
1937 if (_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha / 2)))
1938 {
1939 _joint = PS_JOIN_BEVEL;
1940 }
1941 if (alpha > 0)
1942 {
1943 pInsidePath = pUpPath;
1944 pOutsidePath = pDownPath;
1945 }
1946 else if (alpha < 0)
1947 {
1948 pInsidePath = pDownPath;
1949 pOutsidePath = pUpPath;
1950 }
1951 else
1952 {
1953 continue;
1954 }
1955 /* Inside angle points */
1956 if (alpha > 0)
1957 {
1958 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1959 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1960 }
1961 else
1962 {
1963 pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
1964 pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
1965 }
1966 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1967 if (alpha > 0)
1968 {
1969 pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
1970 pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
1971 }
1972 else
1973 {
1974 pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
1975 pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
1976 }
1977 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1978 /* Outside angle point */
1979 switch(_joint)
1980 {
1981 case PS_JOIN_MITER :
1982 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
1983 pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
1984 pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
1985 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
1986 break;
1987 case PS_JOIN_BEVEL :
1988 if (alpha > 0)
1989 {
1990 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1991 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1992 }
1993 else
1994 {
1995 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
1996 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
1997 }
1998 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
1999 if (alpha > 0)
2000 {
2001 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2002 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2003 }
2004 else
2005 {
2006 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2007 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2008 }
2009 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2010 break;
2011 case PS_JOIN_ROUND :
2012 default :
2013 if (alpha > 0)
2014 {
2015 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2016 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2017 }
2018 else
2019 {
2020 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2021 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2022 }
2023 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2024 pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2025 pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2026 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2027 if (alpha > 0)
2028 {
2029 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2030 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2031 }
2032 else
2033 {
2034 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2035 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2036 }
2037 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2038 break;
2039 }
2040 }
2041 }
2042 for (j = 0; j < pUpPath->numEntriesUsed; j++)
2043 {
2044 POINT pt;
2045 pt.x = pUpPath->pPoints[j].x;
2046 pt.y = pUpPath->pPoints[j].y;
2047 PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
2048 }
2049 for (j = 0; j < pDownPath->numEntriesUsed; j++)
2050 {
2051 POINT pt;
2052 pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
2053 pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
2054 PATH_AddEntry(pNewPath, &pt, ((j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
2055 }
2056
2057 PATH_DestroyGdiPath(pStrokes[i]);
2058 ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2059 PATH_DestroyGdiPath(pUpPath);
2060 ExFreePoolWithTag(pUpPath, TAG_PATH);
2061 PATH_DestroyGdiPath(pDownPath);
2062 ExFreePoolWithTag(pDownPath, TAG_PATH);
2063 }
2064 if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2065
2066 pNewPath->state = PATH_Closed;
2067 if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
2068 DPRINT1("Assign path failed\n");
2069 PATH_DestroyGdiPath(pNewPath);
2070 ExFreePoolWithTag(pNewPath, TAG_PATH);
2071 PATH_UnlockPath(pPath);
2072 return ret;
2073 }
2074
2075 static inline INT int_from_fixed(FIXED f)
2076 {
2077 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2078 }
2079
2080 /**********************************************************************
2081 * PATH_BezierTo
2082 *
2083 * Internally used by PATH_add_outline
2084 */
2085 static
2086 VOID
2087 FASTCALL
2088 PATH_BezierTo(
2089 PPATH pPath,
2090 POINT *lppt,
2091 INT n)
2092 {
2093 if (n < 2) return;
2094
2095 if (n == 2)
2096 {
2097 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2098 }
2099 else if (n == 3)
2100 {
2101 PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
2102 PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
2103 PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
2104 }
2105 else
2106 {
2107 POINT pt[3];
2108 INT i = 0;
2109
2110 pt[2] = lppt[0];
2111 n--;
2112
2113 while (n > 2)
2114 {
2115 pt[0] = pt[2];
2116 pt[1] = lppt[i + 1];
2117 pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2118 pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2119 PATH_BezierTo(pPath, pt, 3);
2120 n--;
2121 i++;
2122 }
2123
2124 pt[0] = pt[2];
2125 pt[1] = lppt[i + 1];
2126 pt[2] = lppt[i + 2];
2127 PATH_BezierTo(pPath, pt, 3);
2128 }
2129 }
2130
2131 static
2132 BOOL
2133 FASTCALL
2134 PATH_add_outline(
2135 PDC dc,
2136 INT x,
2137 INT y,
2138 TTPOLYGONHEADER *header,
2139 DWORD size)
2140 {
2141 PPATH pPath;
2142 TTPOLYGONHEADER *start;
2143 POINT pt;
2144 BOOL bResult = FALSE;
2145
2146 start = header;
2147
2148 pPath = PATH_LockPath(dc->dclevel.hPath);
2149 if (!pPath)
2150 {
2151 return FALSE;
2152 }
2153
2154 while ((char *)header < (char *)start + size)
2155 {
2156 TTPOLYCURVE *curve;
2157
2158 if (header->dwType != TT_POLYGON_TYPE)
2159 {
2160 DPRINT1("Unknown header type %lu\n", header->dwType);
2161 goto cleanup;
2162 }
2163
2164 pt.x = x + int_from_fixed(header->pfxStart.x);
2165 pt.y = y - int_from_fixed(header->pfxStart.y);
2166 PATH_AddEntry(pPath, &pt, PT_MOVETO);
2167
2168 curve = (TTPOLYCURVE *)(header + 1);
2169
2170 while ((char *)curve < (char *)header + header->cb)
2171 {
2172 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2173
2174 switch(curve->wType)
2175 {
2176 case TT_PRIM_LINE:
2177 {
2178 WORD i;
2179
2180 for (i = 0; i < curve->cpfx; i++)
2181 {
2182 pt.x = x + int_from_fixed(curve->apfx[i].x);
2183 pt.y = y - int_from_fixed(curve->apfx[i].y);
2184 PATH_AddEntry(pPath, &pt, PT_LINETO);
2185 }
2186 break;
2187 }
2188
2189 case TT_PRIM_QSPLINE:
2190 case TT_PRIM_CSPLINE:
2191 {
2192 WORD i;
2193 POINTFX ptfx;
2194 POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2195
2196 if (!pts) goto cleanup;
2197
2198 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2199
2200 pts[0].x = x + int_from_fixed(ptfx.x);
2201 pts[0].y = y - int_from_fixed(ptfx.y);
2202
2203 for (i = 0; i < curve->cpfx; i++)
2204 {
2205 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2206 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2207 }
2208
2209 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2210
2211 ExFreePoolWithTag(pts, TAG_PATH);
2212 break;
2213 }
2214
2215 default:
2216 DPRINT1("Unknown curve type %04x\n", curve->wType);
2217 goto cleanup;
2218 }
2219
2220 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2221 }
2222 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2223 }
2224
2225 bResult = TRUE;
2226
2227 cleanup:
2228 IntGdiCloseFigure(pPath);
2229 PATH_UnlockPath(pPath);
2230 return bResult;
2231 }
2232
2233 /**********************************************************************
2234 * PATH_ExtTextOut
2235 */
2236 BOOL
2237 FASTCALL
2238 PATH_ExtTextOut(
2239 PDC dc,
2240 INT x,
2241 INT y,
2242 UINT flags,
2243 const RECTL *lprc,
2244 LPCWSTR str,
2245 UINT count,
2246 const INT *dx)
2247 {
2248 unsigned int idx;
2249 POINT offset = {0, 0};
2250
2251 if (!count) return TRUE;
2252
2253 for (idx = 0; idx < count; idx++)
2254 {
2255 MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2256 GLYPHMETRICS gm;
2257 DWORD dwSize;
2258 void *outline;
2259
2260 dwSize = ftGdiGetGlyphOutline(dc,
2261 str[idx],
2262 GGO_GLYPH_INDEX | GGO_NATIVE,
2263 &gm,
2264 0,
2265 NULL,
2266 &identity,
2267 TRUE);
2268 if (dwSize == GDI_ERROR) return FALSE;
2269
2270 /* Add outline only if char is printable */
2271 if (dwSize)
2272 {
2273 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2274 if (!outline) return FALSE;
2275
2276 ftGdiGetGlyphOutline(dc,
2277 str[idx],
2278 GGO_GLYPH_INDEX | GGO_NATIVE,
2279 &gm,
2280 dwSize,
2281 outline,
2282 &identity,
2283 TRUE);
2284
2285 PATH_add_outline(dc, x + offset.x, y + offset.y, outline, dwSize);
2286
2287 ExFreePoolWithTag(outline, TAG_PATH);
2288 }
2289
2290 if (dx)
2291 {
2292 if (flags & ETO_PDY)
2293 {
2294 offset.x += dx[idx * 2];
2295 offset.y += dx[idx * 2 + 1];
2296 }
2297 else
2298 offset.x += dx[idx];
2299 }
2300 else
2301 {
2302 offset.x += gm.gmCellIncX;
2303 offset.y += gm.gmCellIncY;
2304 }
2305 }
2306 return TRUE;
2307 }
2308
2309
2310 /***********************************************************************
2311 * Exported functions
2312 */
2313
2314 BOOL
2315 APIENTRY
2316 NtGdiAbortPath(HDC hDC)
2317 {
2318 PPATH pPath;
2319 PDC dc = DC_LockDc(hDC);
2320 if (!dc)
2321 {
2322 EngSetLastError(ERROR_INVALID_HANDLE);
2323 return FALSE;
2324 }
2325
2326 pPath = PATH_LockPath(dc->dclevel.hPath);
2327 if (!pPath)
2328 {
2329 DC_UnlockDc(dc);
2330 return FALSE;
2331 }
2332
2333 PATH_EmptyPath(pPath);
2334
2335 PATH_UnlockPath(pPath);
2336 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2337
2338 DC_UnlockDc(dc);
2339 return TRUE;
2340 }
2341
2342 BOOL
2343 APIENTRY
2344 NtGdiBeginPath(HDC hDC)
2345 {
2346 PPATH pPath;
2347 PDC dc;
2348
2349 dc = DC_LockDc(hDC);
2350 if (!dc)
2351 {
2352 EngSetLastError(ERROR_INVALID_HANDLE);
2353 return FALSE;
2354 }
2355
2356 /* If path is already open, do nothing. Check if not Save DC state */
2357 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2358 {
2359 DC_UnlockDc(dc);
2360 return TRUE;
2361 }
2362
2363 if (dc->dclevel.hPath)
2364 {
2365 DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2366 if (!(dc->dclevel.flPath & DCPATH_SAVE))
2367 {
2368 // Remove previous handle.
2369 if (!PATH_Delete(dc->dclevel.hPath))
2370 {
2371 DC_UnlockDc(dc);
2372 return FALSE;
2373 }
2374 }
2375 else
2376 {
2377 // Clear flags and Handle.
2378 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2379 dc->dclevel.hPath = NULL;
2380 }
2381 }
2382 pPath = PATH_AllocPathWithHandle();
2383 if (!pPath)
2384 {
2385 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2386 return FALSE;
2387 }
2388 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2389
2390 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2391
2392 DPRINT("BeginPath 2 h 0x%p p 0x%p\n", dc->dclevel.hPath, pPath);
2393 // Path handles are shared. Also due to recursion with in the same thread.
2394 GDIOBJ_vUnlockObject((POBJ)pPath); // Unlock
2395 pPath = PATH_LockPath(dc->dclevel.hPath); // Share Lock.
2396
2397 /* Make sure that path is empty */
2398 PATH_EmptyPath(pPath);
2399
2400 /* Initialize variables for new path */
2401 pPath->newStroke = TRUE;
2402 pPath->state = PATH_Open;
2403
2404 PATH_UnlockPath(pPath);
2405 DC_UnlockDc(dc);
2406 return TRUE;
2407 }
2408
2409 BOOL
2410 APIENTRY
2411 NtGdiCloseFigure(HDC hDC)
2412 {
2413 BOOL Ret = FALSE; // Default to failure
2414 PDC pDc;
2415 PPATH pPath;
2416
2417 DPRINT("Enter %s\n", __FUNCTION__);
2418
2419 pDc = DC_LockDc(hDC);
2420 if (!pDc)
2421 {
2422 EngSetLastError(ERROR_INVALID_PARAMETER);
2423 return FALSE;
2424 }
2425
2426 pPath = PATH_LockPath(pDc->dclevel.hPath);
2427 if (!pPath)
2428 {
2429 DC_UnlockDc(pDc);
2430 return FALSE;
2431 }
2432
2433 if (pPath->state == PATH_Open)
2434 {
2435 IntGdiCloseFigure(pPath);
2436 Ret = TRUE;
2437 }
2438 else
2439 {
2440 // FIXME: Check if lasterror is set correctly
2441 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2442 }
2443
2444 PATH_UnlockPath(pPath);
2445 DC_UnlockDc(pDc);
2446 return Ret;
2447 }
2448
2449 BOOL
2450 APIENTRY
2451 NtGdiEndPath(HDC hDC)
2452 {
2453 BOOL ret = TRUE;
2454 PPATH pPath;
2455 PDC dc;
2456
2457 dc = DC_LockDc(hDC);
2458 if (!dc)
2459 {
2460 EngSetLastError(ERROR_INVALID_HANDLE);
2461 return FALSE;
2462 }
2463
2464 pPath = PATH_LockPath(dc->dclevel.hPath);
2465 if (!pPath)
2466 {
2467 DC_UnlockDc(dc);
2468 return FALSE;
2469 }
2470
2471 /* Check that path is currently being constructed */
2472 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2473 {
2474 DPRINT1("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2475 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2476 ret = FALSE;
2477 }
2478 /* Set flag to indicate that path is finished */
2479 else
2480 {
2481 DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2482 pPath->state = PATH_Closed;
2483 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2484 }
2485
2486 PATH_UnlockPath(pPath);
2487 DC_UnlockDc(dc);
2488 return ret;
2489 }
2490
2491 BOOL
2492 APIENTRY
2493 NtGdiFillPath(HDC hDC)
2494 {
2495 BOOL ret = FALSE;
2496 PPATH pPath;
2497 PDC_ATTR pdcattr;
2498 PDC dc;
2499
2500 dc = DC_LockDc(hDC);
2501 if (!dc)
2502 {
2503 EngSetLastError(ERROR_INVALID_PARAMETER);
2504 return FALSE;
2505 }
2506
2507 pPath = PATH_LockPath(dc->dclevel.hPath);
2508 if (!pPath)
2509 {
2510 DC_UnlockDc(dc);
2511 return FALSE;
2512 }
2513
2514 DC_vPrepareDCsForBlit(dc, dc->rosdc.CombinedClip->rclBounds,
2515 NULL, dc->rosdc.CombinedClip->rclBounds);
2516
2517 pdcattr = dc->pdcattr;
2518
2519 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2520 DC_vUpdateLineBrush(dc);
2521
2522 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2523 DC_vUpdateFillBrush(dc);
2524
2525 ret = PATH_FillPath(dc, pPath);
2526 if (ret)
2527 {
2528 /* FIXME: Should the path be emptied even if conversion
2529 failed? */
2530 PATH_EmptyPath(pPath);
2531 }
2532
2533 PATH_UnlockPath(pPath);
2534 DC_vFinishBlit(dc, NULL);
2535 DC_UnlockDc(dc);
2536 return ret;
2537 }
2538
2539 BOOL
2540 APIENTRY
2541 NtGdiFlattenPath(HDC hDC)
2542 {
2543 BOOL Ret = FALSE;
2544 DC *pDc;
2545 PPATH pPath;
2546
2547 DPRINT("Enter %s\n", __FUNCTION__);
2548
2549 pDc = DC_LockDc(hDC);
2550 if (!pDc)
2551 {
2552 EngSetLastError(ERROR_INVALID_HANDLE);
2553 return FALSE;
2554 }
2555
2556 pPath = PATH_LockPath(pDc->dclevel.hPath);
2557 if (!pPath)
2558 {
2559 DC_UnlockDc(pDc);
2560 return FALSE;
2561 }
2562 if (pPath->state == PATH_Open)
2563 Ret = PATH_FlattenPath(pPath);
2564
2565 PATH_UnlockPath(pPath);
2566 DC_UnlockDc(pDc);
2567 return Ret;
2568 }
2569
2570 _Success_(return != FALSE)
2571 BOOL
2572 APIENTRY
2573 NtGdiGetMiterLimit(
2574 _In_ HDC hdc,
2575 _Out_ PDWORD pdwOut)
2576 {
2577 DC *pDc;
2578 BOOL bResult = TRUE;
2579
2580 if (!(pDc = DC_LockDc(hdc)))
2581 {
2582 EngSetLastError(ERROR_INVALID_PARAMETER);
2583 return FALSE;
2584 }
2585
2586 _SEH2_TRY
2587 {
2588 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2589 *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2590 }
2591 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2592 {
2593 SetLastNtError(_SEH2_GetExceptionCode());
2594 bResult = FALSE;
2595 }
2596 _SEH2_END;
2597
2598 DC_UnlockDc(pDc);
2599 return bResult;
2600
2601 }
2602
2603 INT
2604 APIENTRY
2605 NtGdiGetPath(
2606 HDC hDC,
2607 LPPOINT Points,
2608 LPBYTE Types,
2609 INT nSize)
2610 {
2611 INT ret = -1;
2612 PPATH pPath;
2613
2614 DC *dc = DC_LockDc(hDC);
2615 if (!dc)
2616 {
2617 DPRINT1("Can't lock dc!\n");
2618 EngSetLastError(ERROR_INVALID_PARAMETER);
2619 return -1;
2620 }
2621
2622 pPath = PATH_LockPath(dc->dclevel.hPath);
2623 if (!pPath)
2624 {
2625 DC_UnlockDc(dc);
2626 return -1;
2627 }
2628
2629 if (pPath->state != PATH_Closed)
2630 {
2631 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2632 goto done;
2633 }
2634
2635 if (nSize == 0)
2636 {
2637 ret = pPath->numEntriesUsed;
2638 }
2639 else if (nSize < pPath->numEntriesUsed)
2640 {
2641 EngSetLastError(ERROR_INVALID_PARAMETER);
2642 goto done;
2643 }
2644 else
2645 {
2646 _SEH2_TRY
2647 {
2648 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2649 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2650
2651 /* Convert the points to logical coordinates */
2652 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2653 {
2654 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2655 _SEH2_LEAVE;
2656 }
2657
2658 ret = pPath->numEntriesUsed;
2659 }
2660 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2661 {
2662 SetLastNtError(_SEH2_GetExceptionCode());
2663 }
2664 _SEH2_END
2665 }
2666
2667 done:
2668 PATH_UnlockPath(pPath);
2669 DC_UnlockDc(dc);
2670 return ret;
2671 }
2672
2673 HRGN
2674 APIENTRY
2675 NtGdiPathToRegion(HDC hDC)
2676 {
2677 PPATH pPath;
2678 HRGN hrgnRval = 0;
2679 DC *pDc;
2680 PDC_ATTR pdcattr;
2681
2682 DPRINT("Enter %s\n", __FUNCTION__);
2683
2684 pDc = DC_LockDc(hDC);
2685 if (!pDc)
2686 {
2687 EngSetLastError(ERROR_INVALID_PARAMETER);
2688 return NULL;
2689 }
2690
2691 pdcattr = pDc->pdcattr;
2692
2693 pPath = PATH_LockPath(pDc->dclevel.hPath);
2694 if (!pPath)
2695 {
2696 DC_UnlockDc(pDc);
2697 return NULL;
2698 }
2699
2700 if (pPath->state != PATH_Closed)
2701 {
2702 // FIXME: Check that setlasterror is being called correctly
2703 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2704 }
2705 else
2706 {
2707 /* FIXME: Should we empty the path even if conversion failed? */
2708 if (PATH_PathToRegion(pPath, pdcattr->jFillMode, &hrgnRval))
2709 PATH_EmptyPath(pPath);
2710 }
2711
2712 PATH_UnlockPath(pPath);
2713 DC_UnlockDc(pDc);
2714 return hrgnRval;
2715 }
2716
2717 BOOL
2718 APIENTRY
2719 NtGdiSetMiterLimit(
2720 IN HDC hdc,
2721 IN DWORD dwNew,
2722 IN OUT OPTIONAL PDWORD pdwOut)
2723 {
2724 DC *pDc;
2725 gxf_long worker, worker1;
2726 BOOL bResult = TRUE;
2727
2728 if (!(pDc = DC_LockDc(hdc)))
2729 {
2730 EngSetLastError(ERROR_INVALID_PARAMETER);
2731 return FALSE;
2732 }
2733
2734 worker.l = dwNew;
2735 worker1.f = pDc->dclevel.laPath.eMiterLimit;
2736 pDc->dclevel.laPath.eMiterLimit = worker.f;
2737
2738 if (pdwOut)
2739 {
2740 _SEH2_TRY
2741 {
2742 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2743 *pdwOut = worker1.l;
2744 }
2745 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2746 {
2747 SetLastNtError(_SEH2_GetExceptionCode());
2748 bResult = FALSE;
2749 }
2750 _SEH2_END;
2751 }
2752
2753 DC_UnlockDc(pDc);
2754 return bResult;
2755 }
2756
2757 BOOL
2758 APIENTRY
2759 NtGdiStrokeAndFillPath(HDC hDC)
2760 {
2761 DC *pDc;
2762 PDC_ATTR pdcattr;
2763 PPATH pPath;
2764 BOOL bRet = FALSE;
2765
2766 DPRINT1("Enter %s\n", __FUNCTION__);
2767
2768 if (!(pDc = DC_LockDc(hDC)))
2769 {
2770 EngSetLastError(ERROR_INVALID_PARAMETER);
2771 return FALSE;
2772 }
2773 pPath = PATH_LockPath(pDc->dclevel.hPath);
2774 if (!pPath)
2775 {
2776 DC_UnlockDc(pDc);
2777 return FALSE;
2778 }
2779
2780 DC_vPrepareDCsForBlit(pDc, pDc->rosdc.CombinedClip->rclBounds,
2781 NULL, pDc->rosdc.CombinedClip->rclBounds);
2782
2783 pdcattr = pDc->pdcattr;
2784
2785 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2786 DC_vUpdateFillBrush(pDc);
2787
2788 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2789 DC_vUpdateLineBrush(pDc);
2790
2791 bRet = PATH_FillPath(pDc, pPath);
2792 if (bRet) bRet = PATH_StrokePath(pDc, pPath);
2793 if (bRet) PATH_EmptyPath(pPath);
2794
2795 PATH_UnlockPath(pPath);
2796 DC_vFinishBlit(pDc, NULL);
2797 DC_UnlockDc(pDc);
2798 return bRet;
2799 }
2800
2801 BOOL
2802 APIENTRY
2803 NtGdiStrokePath(HDC hDC)
2804 {
2805 DC *pDc;
2806 PDC_ATTR pdcattr;
2807 PPATH pPath;
2808 BOOL bRet = FALSE;
2809
2810 DPRINT("Enter %s\n", __FUNCTION__);
2811
2812 if (!(pDc = DC_LockDc(hDC)))
2813 {
2814 EngSetLastError(ERROR_INVALID_PARAMETER);
2815 return FALSE;
2816 }
2817
2818 pPath = PATH_LockPath(pDc->dclevel.hPath);
2819 if (!pPath)
2820 {
2821 DC_UnlockDc(pDc);
2822 return FALSE;
2823 }
2824
2825 DC_vPrepareDCsForBlit(pDc, pDc->rosdc.CombinedClip->rclBounds,
2826 NULL, pDc->rosdc.CombinedClip->rclBounds);
2827
2828 pdcattr = pDc->pdcattr;
2829
2830 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2831 DC_vUpdateLineBrush(pDc);
2832
2833 bRet = PATH_StrokePath(pDc, pPath);
2834
2835 DC_vFinishBlit(pDc, NULL);
2836 PATH_EmptyPath(pPath);
2837
2838 PATH_UnlockPath(pPath);
2839 DC_UnlockDc(pDc);
2840 return bRet;
2841 }
2842
2843 BOOL
2844 APIENTRY
2845 NtGdiWidenPath(HDC hDC)
2846 {
2847 BOOL Ret;
2848 PDC pdc = DC_LockDc(hDC);
2849 if (!pdc)
2850 {
2851 EngSetLastError(ERROR_INVALID_PARAMETER);
2852 return FALSE;
2853 }
2854
2855 Ret = PATH_WidenPath(pdc);
2856 DC_UnlockDc(pdc);
2857 return Ret;
2858 }
2859
2860 /* EOF */