[WIN32K]
[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 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 DPRINT1("numStrokes is 0\n");
1256 return FALSE;
1257 }
1258
1259 /* Allocate memory for number-of-points-in-stroke array */
1260 pNumPointsInStroke = ExAllocatePoolWithTag(PagedPool, sizeof(ULONG) * numStrokes, TAG_PATH);
1261 if (!pNumPointsInStroke)
1262 {
1263 DPRINT1("Failed to allocate %lu strokes\n", numStrokes);
1264 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1265 return FALSE;
1266 }
1267
1268 /* Second pass: remember number of points in each polygon */
1269 iStroke = -1; /* Will get incremented to 0 at beginning of first stroke */
1270 for (i = 0; i < pPath->numEntriesUsed; i++)
1271 {
1272 /* Is this the beginning of a new stroke? */
1273 if ((pPath->pFlags[i] & ~PT_CLOSEFIGURE) == PT_MOVETO)
1274 {
1275 iStroke++;
1276 _PRAGMA_WARNING_SUPPRESS(__WARNING_WRITE_OVERRUN)
1277 pNumPointsInStroke[iStroke] = 0;
1278 }
1279
1280 _PRAGMA_WARNING_SUPPRESS(__WARNING_READ_OVERRUN)
1281 pNumPointsInStroke[iStroke]++;
1282 }
1283
1284 /* Fill the region with the strokes */
1285 Ret = REGION_SetPolyPolygonRgn(Rgn,
1286 pPath->pPoints,
1287 pNumPointsInStroke,
1288 numStrokes,
1289 nPolyFillMode);
1290 if (!Ret)
1291 {
1292 DPRINT1("REGION_SetPolyPolygonRgn failed\n");
1293 }
1294
1295 /* Free memory for number-of-points-in-stroke array */
1296 ExFreePoolWithTag(pNumPointsInStroke, TAG_PATH);
1297
1298 /* Success! */
1299 return Ret;
1300 }
1301
1302 /* PATH_EmptyPath
1303 *
1304 * Removes all entries from the path and sets the path state to PATH_Null.
1305 */
1306 VOID
1307 FASTCALL
1308 PATH_EmptyPath(PPATH pPath)
1309 {
1310 ASSERT(pPath != NULL);
1311
1312 pPath->state = PATH_Null;
1313 pPath->numEntriesUsed = 0;
1314 }
1315
1316 /* PATH_AddEntry
1317 *
1318 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
1319 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
1320 * successful, FALSE otherwise (e.g. if not enough memory was available).
1321 */
1322 BOOL
1323 FASTCALL
1324 PATH_AddEntry(
1325 PPATH pPath,
1326 const POINT *pPoint,
1327 BYTE flags)
1328 {
1329 ASSERT(pPath != NULL);
1330
1331 /* FIXME: If newStroke is true, perhaps we want to check that we're
1332 * getting a PT_MOVETO
1333 */
1334
1335 /* Check that path is open */
1336 if (pPath->state != PATH_Open)
1337 return FALSE;
1338
1339 /* Reserve enough memory for an extra path entry */
1340 if (!PATH_ReserveEntries(pPath, pPath->numEntriesUsed + 1))
1341 return FALSE;
1342
1343 /* Store information in path entry */
1344 pPath->pPoints[pPath->numEntriesUsed] = *pPoint;
1345 pPath->pFlags[pPath->numEntriesUsed] = flags;
1346
1347 /* If this is PT_CLOSEFIGURE, we have to start a new stroke next time */
1348 if ((flags & PT_CLOSEFIGURE) == PT_CLOSEFIGURE)
1349 pPath->newStroke = TRUE;
1350
1351 /* Increment entry count */
1352 pPath->numEntriesUsed++;
1353
1354 return TRUE;
1355 }
1356
1357 /* PATH_ReserveEntries
1358 *
1359 * Ensures that at least "numEntries" entries (for points and flags) have
1360 * been allocated; allocates larger arrays and copies the existing entries
1361 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
1362 */
1363 BOOL
1364 FASTCALL
1365 PATH_ReserveEntries(
1366 PPATH pPath,
1367 INT numEntries)
1368 {
1369 INT numEntriesToAllocate;
1370 POINT *pPointsNew;
1371 BYTE *pFlagsNew;
1372
1373 ASSERT(pPath != NULL);
1374 ASSERT(numEntries >= 0);
1375
1376 /* Do we have to allocate more memory? */
1377 if (numEntries > pPath->numEntriesAllocated)
1378 {
1379 /* Find number of entries to allocate. We let the size of the array
1380 * grow exponentially, since that will guarantee linear time
1381 * complexity. */
1382 if (pPath->numEntriesAllocated)
1383 {
1384 numEntriesToAllocate = pPath->numEntriesAllocated;
1385 while (numEntriesToAllocate < numEntries)
1386 numEntriesToAllocate = numEntriesToAllocate * GROW_FACTOR_NUMER / GROW_FACTOR_DENOM;
1387 }
1388 else
1389 numEntriesToAllocate = numEntries;
1390
1391 /* Allocate new arrays */
1392 pPointsNew = (POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
1393 if (!pPointsNew)
1394 return FALSE;
1395
1396 pFlagsNew = (BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
1397 if (!pFlagsNew)
1398 {
1399 ExFreePoolWithTag(pPointsNew, TAG_PATH);
1400 return FALSE;
1401 }
1402
1403 /* Copy old arrays to new arrays and discard old arrays */
1404 if (pPath->pPoints)
1405 {
1406 ASSERT(pPath->pFlags);
1407
1408 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
1409 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
1410
1411 ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
1412 ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
1413 }
1414
1415 pPath->pPoints = pPointsNew;
1416 pPath->pFlags = pFlagsNew;
1417 pPath->numEntriesAllocated = numEntriesToAllocate;
1418 }
1419
1420 return TRUE;
1421 }
1422
1423 /* PATH_DoArcPart
1424 *
1425 * Creates a Bezier spline that corresponds to part of an arc and appends the
1426 * corresponding points to the path. The start and end angles are passed in
1427 * "angleStart" and "angleEnd"; these angles should span a quarter circle
1428 * at most. If "startEntryType" is non-zero, an entry of that type for the first
1429 * control point is added to the path; otherwise, it is assumed that the current
1430 * position is equal to the first control point.
1431 */
1432 BOOL
1433 FASTCALL
1434 PATH_DoArcPart(
1435 PPATH pPath,
1436 FLOAT_POINT corners[],
1437 double angleStart,
1438 double angleEnd,
1439 BYTE startEntryType)
1440 {
1441 double halfAngle, a;
1442 double xNorm[4], yNorm[4];
1443 POINT point;
1444 int i;
1445
1446 ASSERT(fabs(angleEnd - angleStart) <= M_PI_2);
1447
1448 /* FIXME: Is there an easier way of computing this? */
1449
1450 /* Compute control points */
1451 halfAngle = (angleEnd - angleStart) / 2.0;
1452 if (fabs(halfAngle) > 1e-8)
1453 {
1454 a = 4.0 / 3.0 * (1 - cos(halfAngle)) / sin(halfAngle);
1455 xNorm[0] = cos(angleStart);
1456 yNorm[0] = sin(angleStart);
1457 xNorm[1] = xNorm[0] - a * yNorm[0];
1458 yNorm[1] = yNorm[0] + a * xNorm[0];
1459 xNorm[3] = cos(angleEnd);
1460 yNorm[3] = sin(angleEnd);
1461 xNorm[2] = xNorm[3] + a * yNorm[3];
1462 yNorm[2] = yNorm[3] - a * xNorm[3];
1463 }
1464 else
1465 for (i = 0; i < 4; i++)
1466 {
1467 xNorm[i] = cos(angleStart);
1468 yNorm[i] = sin(angleStart);
1469 }
1470
1471 /* Add starting point to path if desired */
1472 if (startEntryType)
1473 {
1474 PATH_ScaleNormalizedPoint(corners, xNorm[0], yNorm[0], &point);
1475 if (!PATH_AddEntry(pPath, &point, startEntryType))
1476 return FALSE;
1477 }
1478
1479 /* Add remaining control points */
1480 for (i = 1; i < 4; i++)
1481 {
1482 PATH_ScaleNormalizedPoint(corners, xNorm[i], yNorm[i], &point);
1483 if (!PATH_AddEntry(pPath, &point, PT_BEZIERTO))
1484 return FALSE;
1485 }
1486
1487 return TRUE;
1488 }
1489
1490 /* PATH_ScaleNormalizedPoint
1491 *
1492 * Scales a normalized point (x, y) with respect to the box whose corners are
1493 * passed in "corners". The point is stored in "*pPoint". The normalized
1494 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
1495 * (1.0, 1.0) correspond to corners[1].
1496 */
1497 VOID
1498 FASTCALL
1499 PATH_ScaleNormalizedPoint(
1500 FLOAT_POINT corners[],
1501 double x,
1502 double y,
1503 POINT *pPoint)
1504 {
1505 ASSERT(corners);
1506 ASSERT(pPoint);
1507
1508 pPoint->x = GDI_ROUND((double)corners[0].x + (double)(corners[1].x - corners[0].x) * 0.5 * (x + 1.0));
1509 pPoint->y = GDI_ROUND((double)corners[0].y + (double)(corners[1].y - corners[0].y) * 0.5 * (y + 1.0));
1510 }
1511
1512 /* PATH_NormalizePoint
1513 *
1514 * Normalizes a point with respect to the box whose corners are passed in
1515 * corners. The normalized coordinates are stored in *pX and *pY.
1516 */
1517 VOID
1518 FASTCALL
1519 PATH_NormalizePoint(
1520 FLOAT_POINT corners[],
1521 const FLOAT_POINT *pPoint,
1522 double *pX,
1523 double *pY)
1524 {
1525 ASSERT(corners);
1526 ASSERT(pPoint);
1527 ASSERT(pX);
1528 ASSERT(pY);
1529
1530 *pX = (double)(pPoint->x - corners[0].x) / (double)(corners[1].x - corners[0].x) * 2.0 - 1.0;
1531 *pY = (double)(pPoint->y - corners[0].y) / (double)(corners[1].y - corners[0].y) * 2.0 - 1.0;
1532 }
1533
1534
1535 BOOL
1536 FASTCALL
1537 PATH_StrokePath(
1538 DC *dc,
1539 PPATH pPath)
1540 {
1541 BOOL ret = FALSE;
1542 INT i = 0;
1543 INT nLinePts, nAlloc;
1544 POINT *pLinePts = NULL;
1545 POINT ptViewportOrg, ptWindowOrg;
1546 SIZE szViewportExt, szWindowExt;
1547 DWORD mapMode, graphicsMode;
1548 XFORM xform;
1549 PDC_ATTR pdcattr = dc->pdcattr;
1550
1551 DPRINT("Enter %s\n", __FUNCTION__);
1552
1553 if (pPath->state != PATH_Closed)
1554 return FALSE;
1555
1556 /* Save the mapping mode info */
1557 mapMode = pdcattr->iMapMode;
1558
1559 szViewportExt = *DC_pszlViewportExt(dc);
1560 ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1561 szWindowExt = dc->pdcattr->szlWindowExt;
1562 ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1563
1564 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1565
1566 /* Set MM_TEXT */
1567 pdcattr->iMapMode = MM_TEXT;
1568 pdcattr->ptlViewportOrg.x = 0;
1569 pdcattr->ptlViewportOrg.y = 0;
1570 pdcattr->ptlWindowOrg.x = 0;
1571 pdcattr->ptlWindowOrg.y = 0;
1572 graphicsMode = pdcattr->iGraphicsMode;
1573 pdcattr->iGraphicsMode = GM_ADVANCED;
1574 GreModifyWorldTransform(dc, (XFORML*)&xform, MWT_IDENTITY);
1575 pdcattr->iGraphicsMode = graphicsMode;
1576
1577 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1578 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1579 * space in case we get one to keep the number of reallocations small. */
1580 nAlloc = pPath->numEntriesUsed + 1 + 300;
1581 pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1582 if (!pLinePts)
1583 {
1584 DPRINT1("Can't allocate pool!\n");
1585 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1586 goto end;
1587 }
1588 nLinePts = 0;
1589
1590 for (i = 0; i < pPath->numEntriesUsed; i++)
1591 {
1592 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1593 && (pPath->pFlags[i] != PT_MOVETO))
1594 {
1595 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1596 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1597 (INT)pPath->pFlags[i]);
1598 goto end;
1599 }
1600
1601 switch(pPath->pFlags[i])
1602 {
1603 case PT_MOVETO:
1604 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1605 pPath->pPoints[i].x, pPath->pPoints[i].y);
1606 if (nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1607 nLinePts = 0;
1608 pLinePts[nLinePts++] = pPath->pPoints[i];
1609 break;
1610 case PT_LINETO:
1611 case (PT_LINETO | PT_CLOSEFIGURE):
1612 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1613 pPath->pPoints[i].x, pPath->pPoints[i].y);
1614 pLinePts[nLinePts++] = pPath->pPoints[i];
1615 break;
1616 case PT_BEZIERTO:
1617 DPRINT("Got PT_BEZIERTO\n");
1618 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1619 (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1620 {
1621 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1622 ret = FALSE;
1623 goto end;
1624 }
1625 else
1626 {
1627 INT nBzrPts, nMinAlloc;
1628 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i - 1], 4, &nBzrPts);
1629 /* Make sure we have allocated enough memory for the lines of
1630 * this bezier and the rest of the path, assuming we won't get
1631 * another one (since we won't reallocate again then). */
1632 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1633 if (nAlloc < nMinAlloc)
1634 {
1635 // Reallocate memory
1636
1637 POINT *Realloc = NULL;
1638 nAlloc = nMinAlloc * 2;
1639
1640 Realloc = ExAllocatePoolWithTag(PagedPool,
1641 nAlloc * sizeof(POINT),
1642 TAG_PATH);
1643
1644 if (!Realloc)
1645 {
1646 DPRINT1("Can't allocate pool!\n");
1647 goto end;
1648 }
1649
1650 memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1651 ExFreePoolWithTag(pLinePts, TAG_PATH);
1652 pLinePts = Realloc;
1653 }
1654 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1655 nLinePts += nBzrPts - 1;
1656 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1657 i += 2;
1658 }
1659 break;
1660 default:
1661 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1662 goto end;
1663 }
1664
1665 if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1666 {
1667 pLinePts[nLinePts++] = pLinePts[0];
1668 }
1669 }
1670 if (nLinePts >= 2)
1671 IntGdiPolyline(dc, pLinePts, nLinePts);
1672
1673 ret = TRUE;
1674
1675 end:
1676 if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1677
1678 /* Restore the old mapping mode */
1679 pdcattr->iMapMode = mapMode;
1680 pdcattr->szlWindowExt.cx = szWindowExt.cx;
1681 pdcattr->szlWindowExt.cy = szWindowExt.cy;
1682 pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1683 pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1684
1685 pdcattr->szlViewportExt.cx = szViewportExt.cx;
1686 pdcattr->szlViewportExt.cy = szViewportExt.cy;
1687 pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1688 pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1689
1690 /* Restore the world transform */
1691 XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1692
1693 /* If we've moved the current point then get its new position
1694 which will be in device (MM_TEXT) co-ords, convert it to
1695 logical co-ords and re-set it. This basically updates
1696 dc->CurPosX|Y so that their values are in the correct mapping
1697 mode.
1698 */
1699 if (i > 0)
1700 {
1701 POINT pt;
1702 IntGetCurrentPositionEx(dc, &pt);
1703 IntDPtoLP(dc, &pt, 1);
1704 IntGdiMoveToEx(dc, pt.x, pt.y, NULL, FALSE);
1705 }
1706 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1707 return ret;
1708 }
1709
1710 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1711
1712 static
1713 BOOL
1714 FASTCALL
1715 PATH_WidenPath(DC *dc)
1716 {
1717 INT i, j, numStrokes, numOldStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1718 BOOL ret = FALSE;
1719 PPATH pPath, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1720 EXTLOGPEN *elp;
1721 DWORD obj_type, joint, endcap, penType;
1722 PDC_ATTR pdcattr = dc->pdcattr;
1723
1724 pPath = PATH_LockPath(dc->dclevel.hPath);
1725 if (!pPath) return FALSE;
1726
1727 if (pPath->state == PATH_Open)
1728 {
1729 PATH_UnlockPath(pPath);
1730 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1731 return FALSE;
1732 }
1733
1734 PATH_FlattenPath(pPath);
1735
1736 size = GreGetObject(pdcattr->hpen, 0, NULL);
1737 if (!size)
1738 {
1739 PATH_UnlockPath(pPath);
1740 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1741 return FALSE;
1742 }
1743
1744 elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
1745 if (elp == NULL)
1746 {
1747 PATH_UnlockPath(pPath);
1748 EngSetLastError(ERROR_OUTOFMEMORY);
1749 return FALSE;
1750 }
1751
1752 GreGetObject(pdcattr->hpen, size, elp);
1753
1754 obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
1755 if (obj_type == GDI_OBJECT_TYPE_PEN)
1756 {
1757 penStyle = ((LOGPEN*)elp)->lopnStyle;
1758 }
1759 else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
1760 {
1761 penStyle = elp->elpPenStyle;
1762 }
1763 else
1764 {
1765 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1766 ExFreePoolWithTag(elp, TAG_PATH);
1767 PATH_UnlockPath(pPath);
1768 return FALSE;
1769 }
1770
1771 penWidth = elp->elpWidth;
1772 ExFreePoolWithTag(elp, TAG_PATH);
1773
1774 endcap = (PS_ENDCAP_MASK & penStyle);
1775 joint = (PS_JOIN_MASK & penStyle);
1776 penType = (PS_TYPE_MASK & penStyle);
1777
1778 /* The function cannot apply to cosmetic pens */
1779 if (obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
1780 {
1781 PATH_UnlockPath(pPath);
1782 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1783 return FALSE;
1784 }
1785
1786 penWidthIn = penWidth / 2;
1787 penWidthOut = penWidth / 2;
1788 if (penWidthIn + penWidthOut < penWidth)
1789 penWidthOut++;
1790
1791 numStrokes = 0;
1792
1793 for (i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++)
1794 {
1795 POINT point;
1796 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1797 (pPath->pFlags[i] != PT_MOVETO))
1798 {
1799 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1800 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1801 pPath->pFlags[i]);
1802 return FALSE;
1803 }
1804 switch(pPath->pFlags[i])
1805 {
1806 case PT_MOVETO:
1807 if (numStrokes > 0)
1808 {
1809 pStrokes[numStrokes - 1]->state = PATH_Closed;
1810 }
1811 numOldStrokes = numStrokes;
1812 numStrokes++;
1813 j = 0;
1814 if (numStrokes == 1)
1815 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1816 else
1817 {
1818 pOldStrokes = pStrokes; // Save old pointer.
1819 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1820 if (!pStrokes) return FALSE;
1821 RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1822 ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1823 }
1824 if (!pStrokes) return FALSE;
1825 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1826 if (!pStrokes[numStrokes - 1])
1827 {
1828 ASSERT(FALSE); // FIXME
1829 }
1830
1831 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1832 pStrokes[numStrokes - 1]->state = PATH_Open;
1833 case PT_LINETO:
1834 case (PT_LINETO | PT_CLOSEFIGURE):
1835 point.x = pPath->pPoints[i].x;
1836 point.y = pPath->pPoints[i].y;
1837 PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
1838 break;
1839 case PT_BEZIERTO:
1840 /* Should never happen because of the FlattenPath call */
1841 DPRINT1("Should never happen\n");
1842 break;
1843 default:
1844 DPRINT1("Got path flag %c\n", pPath->pFlags[i]);
1845 return FALSE;
1846 }
1847 }
1848
1849 pNewPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1850 if (!pNewPath)
1851 {
1852 ASSERT(FALSE); // FIXME
1853 }
1854 PATH_InitGdiPath(pNewPath);
1855 pNewPath->state = PATH_Open;
1856
1857 for (i = 0; i < numStrokes; i++)
1858 {
1859 pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1860 PATH_InitGdiPath(pUpPath);
1861 pUpPath->state = PATH_Open;
1862 pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1863 PATH_InitGdiPath(pDownPath);
1864 pDownPath->state = PATH_Open;
1865
1866 for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1867 {
1868 /* Beginning or end of the path if not closed */
1869 if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1870 {
1871 /* Compute segment angle */
1872 double xo, yo, xa, ya, theta;
1873 POINT pt;
1874 FLOAT_POINT corners[2];
1875 if (j == 0)
1876 {
1877 xo = pStrokes[i]->pPoints[j].x;
1878 yo = pStrokes[i]->pPoints[j].y;
1879 xa = pStrokes[i]->pPoints[1].x;
1880 ya = pStrokes[i]->pPoints[1].y;
1881 }
1882 else
1883 {
1884 xa = pStrokes[i]->pPoints[j - 1].x;
1885 ya = pStrokes[i]->pPoints[j - 1].y;
1886 xo = pStrokes[i]->pPoints[j].x;
1887 yo = pStrokes[i]->pPoints[j].y;
1888 }
1889 theta = atan2(ya - yo, xa - xo);
1890 switch(endcap)
1891 {
1892 case PS_ENDCAP_SQUARE :
1893 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1894 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1895 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1896 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1897 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1898 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1899 break;
1900 case PS_ENDCAP_FLAT :
1901 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1902 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1903 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1904 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1905 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1906 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1907 break;
1908 case PS_ENDCAP_ROUND :
1909 default :
1910 corners[0].x = xo - penWidthIn;
1911 corners[0].y = yo - penWidthIn;
1912 corners[1].x = xo + penWidthOut;
1913 corners[1].y = yo + penWidthOut;
1914 PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1915 PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1916 PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1917 PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1918 break;
1919 }
1920 }
1921 /* Corpse of the path */
1922 else
1923 {
1924 /* Compute angle */
1925 INT previous, next;
1926 double xa, ya, xb, yb, xo, yo;
1927 double alpha, theta, miterWidth;
1928 DWORD _joint = joint;
1929 POINT pt;
1930 PPATH pInsidePath, pOutsidePath;
1931 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1932 {
1933 previous = j - 1;
1934 next = j + 1;
1935 }
1936 else if (j == 0)
1937 {
1938 previous = pStrokes[i]->numEntriesUsed - 1;
1939 next = j + 1;
1940 }
1941 else
1942 {
1943 previous = j - 1;
1944 next = 0;
1945 }
1946 xo = pStrokes[i]->pPoints[j].x;
1947 yo = pStrokes[i]->pPoints[j].y;
1948 xa = pStrokes[i]->pPoints[previous].x;
1949 ya = pStrokes[i]->pPoints[previous].y;
1950 xb = pStrokes[i]->pPoints[next].x;
1951 yb = pStrokes[i]->pPoints[next].y;
1952 theta = atan2(yo - ya, xo - xa);
1953 alpha = atan2(yb - yo, xb - xo) - theta;
1954 if (alpha > 0) alpha -= M_PI;
1955 else alpha += M_PI;
1956 if (_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha / 2)))
1957 {
1958 _joint = PS_JOIN_BEVEL;
1959 }
1960 if (alpha > 0)
1961 {
1962 pInsidePath = pUpPath;
1963 pOutsidePath = pDownPath;
1964 }
1965 else if (alpha < 0)
1966 {
1967 pInsidePath = pDownPath;
1968 pOutsidePath = pUpPath;
1969 }
1970 else
1971 {
1972 continue;
1973 }
1974 /* Inside angle points */
1975 if (alpha > 0)
1976 {
1977 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1978 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1979 }
1980 else
1981 {
1982 pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
1983 pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
1984 }
1985 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1986 if (alpha > 0)
1987 {
1988 pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
1989 pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
1990 }
1991 else
1992 {
1993 pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
1994 pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
1995 }
1996 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1997 /* Outside angle point */
1998 switch(_joint)
1999 {
2000 case PS_JOIN_MITER :
2001 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2002 pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
2003 pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
2004 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2005 break;
2006 case PS_JOIN_BEVEL :
2007 if (alpha > 0)
2008 {
2009 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2010 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2011 }
2012 else
2013 {
2014 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2015 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2016 }
2017 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2018 if (alpha > 0)
2019 {
2020 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2021 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2022 }
2023 else
2024 {
2025 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2026 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2027 }
2028 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2029 break;
2030 case PS_JOIN_ROUND :
2031 default :
2032 if (alpha > 0)
2033 {
2034 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2035 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2036 }
2037 else
2038 {
2039 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2040 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2041 }
2042 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2043 pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2044 pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2045 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2046 if (alpha > 0)
2047 {
2048 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2049 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2050 }
2051 else
2052 {
2053 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2054 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2055 }
2056 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2057 break;
2058 }
2059 }
2060 }
2061 for (j = 0; j < pUpPath->numEntriesUsed; j++)
2062 {
2063 POINT pt;
2064 pt.x = pUpPath->pPoints[j].x;
2065 pt.y = pUpPath->pPoints[j].y;
2066 PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
2067 }
2068 for (j = 0; j < pDownPath->numEntriesUsed; j++)
2069 {
2070 POINT pt;
2071 pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
2072 pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
2073 PATH_AddEntry(pNewPath, &pt, ((j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
2074 }
2075
2076 PATH_DestroyGdiPath(pStrokes[i]);
2077 ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2078 PATH_DestroyGdiPath(pUpPath);
2079 ExFreePoolWithTag(pUpPath, TAG_PATH);
2080 PATH_DestroyGdiPath(pDownPath);
2081 ExFreePoolWithTag(pDownPath, TAG_PATH);
2082 }
2083 if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2084
2085 pNewPath->state = PATH_Closed;
2086 if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
2087 DPRINT1("Assign path failed\n");
2088 PATH_DestroyGdiPath(pNewPath);
2089 ExFreePoolWithTag(pNewPath, TAG_PATH);
2090 PATH_UnlockPath(pPath);
2091 return ret;
2092 }
2093
2094 static inline INT int_from_fixed(FIXED f)
2095 {
2096 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2097 }
2098
2099 /**********************************************************************
2100 * PATH_BezierTo
2101 *
2102 * Internally used by PATH_add_outline
2103 */
2104 static
2105 VOID
2106 FASTCALL
2107 PATH_BezierTo(
2108 PPATH pPath,
2109 POINT *lppt,
2110 INT n)
2111 {
2112 if (n < 2) return;
2113
2114 if (n == 2)
2115 {
2116 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2117 }
2118 else if (n == 3)
2119 {
2120 PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
2121 PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
2122 PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
2123 }
2124 else
2125 {
2126 POINT pt[3];
2127 INT i = 0;
2128
2129 pt[2] = lppt[0];
2130 n--;
2131
2132 while (n > 2)
2133 {
2134 pt[0] = pt[2];
2135 pt[1] = lppt[i + 1];
2136 pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2137 pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2138 PATH_BezierTo(pPath, pt, 3);
2139 n--;
2140 i++;
2141 }
2142
2143 pt[0] = pt[2];
2144 pt[1] = lppt[i + 1];
2145 pt[2] = lppt[i + 2];
2146 PATH_BezierTo(pPath, pt, 3);
2147 }
2148 }
2149
2150 static
2151 BOOL
2152 FASTCALL
2153 PATH_add_outline(
2154 PDC dc,
2155 INT x,
2156 INT y,
2157 TTPOLYGONHEADER *header,
2158 DWORD size)
2159 {
2160 PPATH pPath;
2161 TTPOLYGONHEADER *start;
2162 POINT pt;
2163 BOOL bResult = FALSE;
2164
2165 start = header;
2166
2167 pPath = PATH_LockPath(dc->dclevel.hPath);
2168 if (!pPath)
2169 {
2170 return FALSE;
2171 }
2172
2173 while ((char *)header < (char *)start + size)
2174 {
2175 TTPOLYCURVE *curve;
2176
2177 if (header->dwType != TT_POLYGON_TYPE)
2178 {
2179 DPRINT1("Unknown header type %lu\n", header->dwType);
2180 goto cleanup;
2181 }
2182
2183 pt.x = x + int_from_fixed(header->pfxStart.x);
2184 pt.y = y - int_from_fixed(header->pfxStart.y);
2185 PATH_AddEntry(pPath, &pt, PT_MOVETO);
2186
2187 curve = (TTPOLYCURVE *)(header + 1);
2188
2189 while ((char *)curve < (char *)header + header->cb)
2190 {
2191 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2192
2193 switch(curve->wType)
2194 {
2195 case TT_PRIM_LINE:
2196 {
2197 WORD i;
2198
2199 for (i = 0; i < curve->cpfx; i++)
2200 {
2201 pt.x = x + int_from_fixed(curve->apfx[i].x);
2202 pt.y = y - int_from_fixed(curve->apfx[i].y);
2203 PATH_AddEntry(pPath, &pt, PT_LINETO);
2204 }
2205 break;
2206 }
2207
2208 case TT_PRIM_QSPLINE:
2209 case TT_PRIM_CSPLINE:
2210 {
2211 WORD i;
2212 POINTFX ptfx;
2213 POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2214
2215 if (!pts) goto cleanup;
2216
2217 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2218
2219 pts[0].x = x + int_from_fixed(ptfx.x);
2220 pts[0].y = y - int_from_fixed(ptfx.y);
2221
2222 for (i = 0; i < curve->cpfx; i++)
2223 {
2224 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2225 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2226 }
2227
2228 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2229
2230 ExFreePoolWithTag(pts, TAG_PATH);
2231 break;
2232 }
2233
2234 default:
2235 DPRINT1("Unknown curve type %04x\n", curve->wType);
2236 goto cleanup;
2237 }
2238
2239 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2240 }
2241 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2242 }
2243
2244 bResult = TRUE;
2245
2246 cleanup:
2247 IntGdiCloseFigure(pPath);
2248 PATH_UnlockPath(pPath);
2249 return bResult;
2250 }
2251
2252 /**********************************************************************
2253 * PATH_ExtTextOut
2254 */
2255 BOOL
2256 FASTCALL
2257 PATH_ExtTextOut(
2258 PDC dc,
2259 INT x,
2260 INT y,
2261 UINT flags,
2262 const RECTL *lprc,
2263 LPCWSTR str,
2264 UINT count,
2265 const INT *dx)
2266 {
2267 unsigned int idx;
2268 POINT offset = {0, 0};
2269
2270 if (!count) return TRUE;
2271
2272 for (idx = 0; idx < count; idx++)
2273 {
2274 MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2275 GLYPHMETRICS gm;
2276 DWORD dwSize;
2277 void *outline;
2278
2279 dwSize = ftGdiGetGlyphOutline(dc,
2280 str[idx],
2281 GGO_GLYPH_INDEX | GGO_NATIVE,
2282 &gm,
2283 0,
2284 NULL,
2285 &identity,
2286 TRUE);
2287 if (dwSize == GDI_ERROR) return FALSE;
2288
2289 /* Add outline only if char is printable */
2290 if (dwSize)
2291 {
2292 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2293 if (!outline) return FALSE;
2294
2295 ftGdiGetGlyphOutline(dc,
2296 str[idx],
2297 GGO_GLYPH_INDEX | GGO_NATIVE,
2298 &gm,
2299 dwSize,
2300 outline,
2301 &identity,
2302 TRUE);
2303
2304 PATH_add_outline(dc, x + offset.x, y + offset.y, outline, dwSize);
2305
2306 ExFreePoolWithTag(outline, TAG_PATH);
2307 }
2308
2309 if (dx)
2310 {
2311 if (flags & ETO_PDY)
2312 {
2313 offset.x += dx[idx * 2];
2314 offset.y += dx[idx * 2 + 1];
2315 }
2316 else
2317 offset.x += dx[idx];
2318 }
2319 else
2320 {
2321 offset.x += gm.gmCellIncX;
2322 offset.y += gm.gmCellIncY;
2323 }
2324 }
2325 return TRUE;
2326 }
2327
2328
2329 /***********************************************************************
2330 * Exported functions
2331 */
2332
2333 BOOL
2334 APIENTRY
2335 NtGdiAbortPath(HDC hDC)
2336 {
2337 PPATH pPath;
2338 PDC dc = DC_LockDc(hDC);
2339 if (!dc)
2340 {
2341 EngSetLastError(ERROR_INVALID_HANDLE);
2342 return FALSE;
2343 }
2344
2345 pPath = PATH_LockPath(dc->dclevel.hPath);
2346 if (!pPath)
2347 {
2348 DC_UnlockDc(dc);
2349 return FALSE;
2350 }
2351
2352 PATH_EmptyPath(pPath);
2353
2354 PATH_UnlockPath(pPath);
2355 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2356
2357 DC_UnlockDc(dc);
2358 return TRUE;
2359 }
2360
2361 BOOL
2362 APIENTRY
2363 NtGdiBeginPath(HDC hDC)
2364 {
2365 PPATH pPath;
2366 PDC dc;
2367
2368 dc = DC_LockDc(hDC);
2369 if (!dc)
2370 {
2371 EngSetLastError(ERROR_INVALID_HANDLE);
2372 return FALSE;
2373 }
2374
2375 /* If path is already open, do nothing. Check if not Save DC state */
2376 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2377 {
2378 DC_UnlockDc(dc);
2379 return TRUE;
2380 }
2381
2382 if (dc->dclevel.hPath)
2383 {
2384 DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2385 if (!(dc->dclevel.flPath & DCPATH_SAVE))
2386 {
2387 // Remove previous handle.
2388 if (!PATH_Delete(dc->dclevel.hPath))
2389 {
2390 DC_UnlockDc(dc);
2391 return FALSE;
2392 }
2393 }
2394 else
2395 {
2396 // Clear flags and Handle.
2397 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2398 dc->dclevel.hPath = NULL;
2399 }
2400 }
2401 pPath = PATH_AllocPathWithHandle();
2402 if (!pPath)
2403 {
2404 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2405 return FALSE;
2406 }
2407 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2408
2409 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2410
2411 DPRINT("BeginPath 2 h 0x%p p 0x%p\n", dc->dclevel.hPath, pPath);
2412 // Path handles are shared. Also due to recursion with in the same thread.
2413 GDIOBJ_vUnlockObject((POBJ)pPath); // Unlock
2414 pPath = PATH_LockPath(dc->dclevel.hPath); // Share Lock.
2415
2416 /* Make sure that path is empty */
2417 PATH_EmptyPath(pPath);
2418
2419 /* Initialize variables for new path */
2420 pPath->newStroke = TRUE;
2421 pPath->state = PATH_Open;
2422
2423 PATH_UnlockPath(pPath);
2424 DC_UnlockDc(dc);
2425 return TRUE;
2426 }
2427
2428 BOOL
2429 APIENTRY
2430 NtGdiCloseFigure(HDC hDC)
2431 {
2432 BOOL Ret = FALSE; // Default to failure
2433 PDC pDc;
2434 PPATH pPath;
2435
2436 DPRINT("Enter %s\n", __FUNCTION__);
2437
2438 pDc = DC_LockDc(hDC);
2439 if (!pDc)
2440 {
2441 EngSetLastError(ERROR_INVALID_PARAMETER);
2442 return FALSE;
2443 }
2444
2445 pPath = PATH_LockPath(pDc->dclevel.hPath);
2446 if (!pPath)
2447 {
2448 DC_UnlockDc(pDc);
2449 return FALSE;
2450 }
2451
2452 if (pPath->state == PATH_Open)
2453 {
2454 IntGdiCloseFigure(pPath);
2455 Ret = TRUE;
2456 }
2457 else
2458 {
2459 // FIXME: Check if lasterror is set correctly
2460 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2461 }
2462
2463 PATH_UnlockPath(pPath);
2464 DC_UnlockDc(pDc);
2465 return Ret;
2466 }
2467
2468 BOOL
2469 APIENTRY
2470 NtGdiEndPath(HDC hDC)
2471 {
2472 BOOL ret = TRUE;
2473 PPATH pPath;
2474 PDC dc;
2475
2476 dc = DC_LockDc(hDC);
2477 if (!dc)
2478 {
2479 EngSetLastError(ERROR_INVALID_HANDLE);
2480 return FALSE;
2481 }
2482
2483 pPath = PATH_LockPath(dc->dclevel.hPath);
2484 if (!pPath)
2485 {
2486 DC_UnlockDc(dc);
2487 return FALSE;
2488 }
2489
2490 /* Check that path is currently being constructed */
2491 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2492 {
2493 DPRINT1("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2494 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2495 ret = FALSE;
2496 }
2497 /* Set flag to indicate that path is finished */
2498 else
2499 {
2500 DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2501 pPath->state = PATH_Closed;
2502 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2503 }
2504
2505 PATH_UnlockPath(pPath);
2506 DC_UnlockDc(dc);
2507 return ret;
2508 }
2509
2510 BOOL
2511 APIENTRY
2512 NtGdiFillPath(HDC hDC)
2513 {
2514 BOOL ret = FALSE;
2515 PPATH pPath;
2516 PDC_ATTR pdcattr;
2517 PDC dc;
2518
2519 dc = DC_LockDc(hDC);
2520 if (!dc)
2521 {
2522 EngSetLastError(ERROR_INVALID_PARAMETER);
2523 return FALSE;
2524 }
2525
2526 pPath = PATH_LockPath(dc->dclevel.hPath);
2527 if (!pPath)
2528 {
2529 DC_UnlockDc(dc);
2530 return FALSE;
2531 }
2532
2533 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2534
2535 pdcattr = dc->pdcattr;
2536
2537 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2538 DC_vUpdateLineBrush(dc);
2539
2540 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2541 DC_vUpdateFillBrush(dc);
2542
2543 ret = PATH_FillPath(dc, pPath);
2544 if (ret)
2545 {
2546 /* FIXME: Should the path be emptied even if conversion
2547 failed? */
2548 PATH_EmptyPath(pPath);
2549 }
2550
2551 PATH_UnlockPath(pPath);
2552 DC_vFinishBlit(dc, NULL);
2553 DC_UnlockDc(dc);
2554 return ret;
2555 }
2556
2557 BOOL
2558 APIENTRY
2559 NtGdiFlattenPath(HDC hDC)
2560 {
2561 BOOL Ret = FALSE;
2562 DC *pDc;
2563 PPATH pPath;
2564
2565 DPRINT("Enter %s\n", __FUNCTION__);
2566
2567 pDc = DC_LockDc(hDC);
2568 if (!pDc)
2569 {
2570 EngSetLastError(ERROR_INVALID_HANDLE);
2571 return FALSE;
2572 }
2573
2574 pPath = PATH_LockPath(pDc->dclevel.hPath);
2575 if (!pPath)
2576 {
2577 DC_UnlockDc(pDc);
2578 return FALSE;
2579 }
2580 if (pPath->state == PATH_Open)
2581 Ret = PATH_FlattenPath(pPath);
2582
2583 PATH_UnlockPath(pPath);
2584 DC_UnlockDc(pDc);
2585 return Ret;
2586 }
2587
2588 _Success_(return != FALSE)
2589 BOOL
2590 APIENTRY
2591 NtGdiGetMiterLimit(
2592 _In_ HDC hdc,
2593 _Out_ PDWORD pdwOut)
2594 {
2595 DC *pDc;
2596 BOOL bResult = TRUE;
2597
2598 if (!(pDc = DC_LockDc(hdc)))
2599 {
2600 EngSetLastError(ERROR_INVALID_PARAMETER);
2601 return FALSE;
2602 }
2603
2604 _SEH2_TRY
2605 {
2606 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2607 *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2608 }
2609 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2610 {
2611 SetLastNtError(_SEH2_GetExceptionCode());
2612 bResult = FALSE;
2613 }
2614 _SEH2_END;
2615
2616 DC_UnlockDc(pDc);
2617 return bResult;
2618
2619 }
2620
2621 INT
2622 APIENTRY
2623 NtGdiGetPath(
2624 HDC hDC,
2625 LPPOINT Points,
2626 LPBYTE Types,
2627 INT nSize)
2628 {
2629 INT ret = -1;
2630 PPATH pPath;
2631
2632 DC *dc = DC_LockDc(hDC);
2633 if (!dc)
2634 {
2635 DPRINT1("Can't lock dc!\n");
2636 EngSetLastError(ERROR_INVALID_PARAMETER);
2637 return -1;
2638 }
2639
2640 pPath = PATH_LockPath(dc->dclevel.hPath);
2641 if (!pPath)
2642 {
2643 DC_UnlockDc(dc);
2644 return -1;
2645 }
2646
2647 if (pPath->state != PATH_Closed)
2648 {
2649 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2650 goto done;
2651 }
2652
2653 if (nSize == 0)
2654 {
2655 ret = pPath->numEntriesUsed;
2656 }
2657 else if (nSize < pPath->numEntriesUsed)
2658 {
2659 EngSetLastError(ERROR_INVALID_PARAMETER);
2660 goto done;
2661 }
2662 else
2663 {
2664 _SEH2_TRY
2665 {
2666 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2667 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2668
2669 /* Convert the points to logical coordinates */
2670 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2671 {
2672 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2673 _SEH2_LEAVE;
2674 }
2675
2676 ret = pPath->numEntriesUsed;
2677 }
2678 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2679 {
2680 SetLastNtError(_SEH2_GetExceptionCode());
2681 }
2682 _SEH2_END
2683 }
2684
2685 done:
2686 PATH_UnlockPath(pPath);
2687 DC_UnlockDc(dc);
2688 return ret;
2689 }
2690
2691 HRGN
2692 APIENTRY
2693 NtGdiPathToRegion(HDC hDC)
2694 {
2695 PPATH pPath;
2696 HRGN hrgnRval = 0;
2697 PREGION Rgn;
2698 DC *pDc;
2699 PDC_ATTR pdcattr;
2700
2701 DPRINT("Enter %s\n", __FUNCTION__);
2702
2703 pDc = DC_LockDc(hDC);
2704 if (!pDc)
2705 {
2706 EngSetLastError(ERROR_INVALID_PARAMETER);
2707 return NULL;
2708 }
2709
2710 pdcattr = pDc->pdcattr;
2711
2712 pPath = PATH_LockPath(pDc->dclevel.hPath);
2713 if (!pPath)
2714 {
2715 DC_UnlockDc(pDc);
2716 return NULL;
2717 }
2718
2719 if (pPath->state != PATH_Closed)
2720 {
2721 // FIXME: Check that setlasterror is being called correctly
2722 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2723 }
2724 else
2725 {
2726 /* Create the region and fill it with the path strokes */
2727 Rgn = REGION_AllocUserRgnWithHandle(1);
2728 if (!Rgn)
2729 {
2730 PATH_UnlockPath(pPath);
2731 DC_UnlockDc(pDc);
2732 return NULL;
2733 }
2734 hrgnRval = Rgn->BaseObject.hHmgr;
2735 /* FIXME: Should we empty the path even if conversion failed? */
2736 if (PATH_PathToRegion(pPath, pdcattr->jFillMode, Rgn))
2737 {
2738 PATH_EmptyPath(pPath);
2739 REGION_UnlockRgn(Rgn);
2740 }
2741 else
2742 {
2743 REGION_Delete(Rgn);
2744 hrgnRval = NULL;
2745 }
2746 }
2747
2748 PATH_UnlockPath(pPath);
2749 DC_UnlockDc(pDc);
2750 return hrgnRval;
2751 }
2752
2753 BOOL
2754 APIENTRY
2755 NtGdiSetMiterLimit(
2756 IN HDC hdc,
2757 IN DWORD dwNew,
2758 IN OUT OPTIONAL PDWORD pdwOut)
2759 {
2760 DC *pDc;
2761 gxf_long worker, worker1;
2762 BOOL bResult = TRUE;
2763
2764 if (!(pDc = DC_LockDc(hdc)))
2765 {
2766 EngSetLastError(ERROR_INVALID_PARAMETER);
2767 return FALSE;
2768 }
2769
2770 worker.l = dwNew;
2771 worker1.f = pDc->dclevel.laPath.eMiterLimit;
2772 pDc->dclevel.laPath.eMiterLimit = worker.f;
2773
2774 if (pdwOut)
2775 {
2776 _SEH2_TRY
2777 {
2778 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2779 *pdwOut = worker1.l;
2780 }
2781 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2782 {
2783 SetLastNtError(_SEH2_GetExceptionCode());
2784 bResult = FALSE;
2785 }
2786 _SEH2_END;
2787 }
2788
2789 DC_UnlockDc(pDc);
2790 return bResult;
2791 }
2792
2793 BOOL
2794 APIENTRY
2795 NtGdiStrokeAndFillPath(HDC hDC)
2796 {
2797 DC *pDc;
2798 PDC_ATTR pdcattr;
2799 PPATH pPath;
2800 BOOL bRet = FALSE;
2801
2802 DPRINT1("Enter %s\n", __FUNCTION__);
2803
2804 if (!(pDc = DC_LockDc(hDC)))
2805 {
2806 EngSetLastError(ERROR_INVALID_PARAMETER);
2807 return FALSE;
2808 }
2809 pPath = PATH_LockPath(pDc->dclevel.hPath);
2810 if (!pPath)
2811 {
2812 DC_UnlockDc(pDc);
2813 return FALSE;
2814 }
2815
2816 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2817
2818 pdcattr = pDc->pdcattr;
2819
2820 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2821 DC_vUpdateFillBrush(pDc);
2822
2823 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2824 DC_vUpdateLineBrush(pDc);
2825
2826 bRet = PATH_FillPath(pDc, pPath);
2827 if (bRet) bRet = PATH_StrokePath(pDc, pPath);
2828 if (bRet) PATH_EmptyPath(pPath);
2829
2830 PATH_UnlockPath(pPath);
2831 DC_vFinishBlit(pDc, NULL);
2832 DC_UnlockDc(pDc);
2833 return bRet;
2834 }
2835
2836 BOOL
2837 APIENTRY
2838 NtGdiStrokePath(HDC hDC)
2839 {
2840 DC *pDc;
2841 PDC_ATTR pdcattr;
2842 PPATH pPath;
2843 BOOL bRet = FALSE;
2844
2845 DPRINT("Enter %s\n", __FUNCTION__);
2846
2847 if (!(pDc = DC_LockDc(hDC)))
2848 {
2849 EngSetLastError(ERROR_INVALID_PARAMETER);
2850 return FALSE;
2851 }
2852
2853 pPath = PATH_LockPath(pDc->dclevel.hPath);
2854 if (!pPath)
2855 {
2856 DC_UnlockDc(pDc);
2857 return FALSE;
2858 }
2859
2860 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2861
2862 pdcattr = pDc->pdcattr;
2863
2864 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2865 DC_vUpdateLineBrush(pDc);
2866
2867 bRet = PATH_StrokePath(pDc, pPath);
2868
2869 DC_vFinishBlit(pDc, NULL);
2870 PATH_EmptyPath(pPath);
2871
2872 PATH_UnlockPath(pPath);
2873 DC_UnlockDc(pDc);
2874 return bRet;
2875 }
2876
2877 BOOL
2878 APIENTRY
2879 NtGdiWidenPath(HDC hDC)
2880 {
2881 BOOL Ret;
2882 PDC pdc = DC_LockDc(hDC);
2883 if (!pdc)
2884 {
2885 EngSetLastError(ERROR_INVALID_PARAMETER);
2886 return FALSE;
2887 }
2888
2889 Ret = PATH_WidenPath(pdc);
2890 DC_UnlockDc(pdc);
2891 return Ret;
2892 }
2893
2894 /* EOF */