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