[NtGdi]
[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: win32ss/gdi/ntgdi/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 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1648 goto end;
1649 }
1650
1651 memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1652 ExFreePoolWithTag(pLinePts, TAG_PATH);
1653 pLinePts = Realloc;
1654 }
1655 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1656 nLinePts += nBzrPts - 1;
1657 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1658 i += 2;
1659 }
1660 break;
1661 default:
1662 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1663 goto end;
1664 }
1665
1666 if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1667 {
1668 pLinePts[nLinePts++] = pLinePts[0];
1669 }
1670 }
1671 if (nLinePts >= 2)
1672 IntGdiPolyline(dc, pLinePts, nLinePts);
1673
1674 ret = TRUE;
1675
1676 end:
1677 if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1678
1679 /* Restore the old mapping mode */
1680 pdcattr->iMapMode = mapMode;
1681 pdcattr->szlWindowExt.cx = szWindowExt.cx;
1682 pdcattr->szlWindowExt.cy = szWindowExt.cy;
1683 pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1684 pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1685
1686 pdcattr->szlViewportExt.cx = szViewportExt.cx;
1687 pdcattr->szlViewportExt.cy = szViewportExt.cy;
1688 pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1689 pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1690
1691 /* Restore the world transform */
1692 XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1693
1694 /* If we've moved the current point then get its new position
1695 which will be in device (MM_TEXT) co-ords, convert it to
1696 logical co-ords and re-set it. This basically updates
1697 dc->CurPosX|Y so that their values are in the correct mapping
1698 mode.
1699 */
1700 if (i > 0)
1701 {
1702 POINT pt;
1703 IntGetCurrentPositionEx(dc, &pt);
1704 IntDPtoLP(dc, &pt, 1);
1705 IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1706 }
1707 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1708 return ret;
1709 }
1710
1711 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1712
1713 static
1714 BOOL
1715 FASTCALL
1716 PATH_WidenPath(DC *dc)
1717 {
1718 INT i, j, numStrokes, numOldStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1719 BOOL ret = FALSE;
1720 PPATH pPath, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1721 EXTLOGPEN *elp;
1722 DWORD obj_type, joint, endcap, penType;
1723 PDC_ATTR pdcattr = dc->pdcattr;
1724
1725 pPath = PATH_LockPath(dc->dclevel.hPath);
1726 if (!pPath) return FALSE;
1727
1728 if (pPath->state == PATH_Open)
1729 {
1730 PATH_UnlockPath(pPath);
1731 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1732 return FALSE;
1733 }
1734
1735 PATH_FlattenPath(pPath);
1736
1737 size = GreGetObject(pdcattr->hpen, 0, NULL);
1738 if (!size)
1739 {
1740 PATH_UnlockPath(pPath);
1741 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1742 return FALSE;
1743 }
1744
1745 elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
1746 if (elp == NULL)
1747 {
1748 PATH_UnlockPath(pPath);
1749 EngSetLastError(ERROR_OUTOFMEMORY);
1750 return FALSE;
1751 }
1752
1753 GreGetObject(pdcattr->hpen, size, elp);
1754
1755 obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
1756 if (obj_type == GDI_OBJECT_TYPE_PEN)
1757 {
1758 penStyle = ((LOGPEN*)elp)->lopnStyle;
1759 }
1760 else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
1761 {
1762 penStyle = elp->elpPenStyle;
1763 }
1764 else
1765 {
1766 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1767 ExFreePoolWithTag(elp, TAG_PATH);
1768 PATH_UnlockPath(pPath);
1769 return FALSE;
1770 }
1771
1772 penWidth = elp->elpWidth;
1773 ExFreePoolWithTag(elp, TAG_PATH);
1774
1775 endcap = (PS_ENDCAP_MASK & penStyle);
1776 joint = (PS_JOIN_MASK & penStyle);
1777 penType = (PS_TYPE_MASK & penStyle);
1778
1779 /* The function cannot apply to cosmetic pens */
1780 if (obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
1781 {
1782 PATH_UnlockPath(pPath);
1783 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1784 return FALSE;
1785 }
1786
1787 penWidthIn = penWidth / 2;
1788 penWidthOut = penWidth / 2;
1789 if (penWidthIn + penWidthOut < penWidth)
1790 penWidthOut++;
1791
1792 numStrokes = 0;
1793
1794 for (i = 0, j = 0; i < pPath->numEntriesUsed; i++, j++)
1795 {
1796 POINT point;
1797 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1798 (pPath->pFlags[i] != PT_MOVETO))
1799 {
1800 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1801 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1802 pPath->pFlags[i]);
1803 return FALSE;
1804 }
1805 switch(pPath->pFlags[i])
1806 {
1807 case PT_MOVETO:
1808 if (numStrokes > 0)
1809 {
1810 pStrokes[numStrokes - 1]->state = PATH_Closed;
1811 }
1812 numOldStrokes = numStrokes;
1813 numStrokes++;
1814 j = 0;
1815 if (numStrokes == 1)
1816 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1817 else
1818 {
1819 pOldStrokes = pStrokes; // Save old pointer.
1820 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(PPATH), TAG_PATH);
1821 if (!pStrokes) return FALSE;
1822 RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1823 ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1824 }
1825 if (!pStrokes) return FALSE;
1826 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1827 if (!pStrokes[numStrokes - 1])
1828 {
1829 ASSERT(FALSE); // FIXME
1830 }
1831
1832 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1833 pStrokes[numStrokes - 1]->state = PATH_Open;
1834 case PT_LINETO:
1835 case (PT_LINETO | PT_CLOSEFIGURE):
1836 point.x = pPath->pPoints[i].x;
1837 point.y = pPath->pPoints[i].y;
1838 PATH_AddEntry(pStrokes[numStrokes - 1], &point, pPath->pFlags[i]);
1839 break;
1840 case PT_BEZIERTO:
1841 /* Should never happen because of the FlattenPath call */
1842 DPRINT1("Should never happen\n");
1843 break;
1844 default:
1845 DPRINT1("Got path flag %c\n", pPath->pFlags[i]);
1846 return FALSE;
1847 }
1848 }
1849
1850 pNewPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1851 if (!pNewPath)
1852 {
1853 ASSERT(FALSE); // FIXME
1854 }
1855 PATH_InitGdiPath(pNewPath);
1856 pNewPath->state = PATH_Open;
1857
1858 for (i = 0; i < numStrokes; i++)
1859 {
1860 pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1861 PATH_InitGdiPath(pUpPath);
1862 pUpPath->state = PATH_Open;
1863 pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1864 PATH_InitGdiPath(pDownPath);
1865 pDownPath->state = PATH_Open;
1866
1867 for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1868 {
1869 /* Beginning or end of the path if not closed */
1870 if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1871 {
1872 /* Compute segment angle */
1873 double xo, yo, xa, ya, theta;
1874 POINT pt;
1875 FLOAT_POINT corners[2];
1876 if (j == 0)
1877 {
1878 xo = pStrokes[i]->pPoints[j].x;
1879 yo = pStrokes[i]->pPoints[j].y;
1880 xa = pStrokes[i]->pPoints[1].x;
1881 ya = pStrokes[i]->pPoints[1].y;
1882 }
1883 else
1884 {
1885 xa = pStrokes[i]->pPoints[j - 1].x;
1886 ya = pStrokes[i]->pPoints[j - 1].y;
1887 xo = pStrokes[i]->pPoints[j].x;
1888 yo = pStrokes[i]->pPoints[j].y;
1889 }
1890 theta = atan2(ya - yo, xa - xo);
1891 switch(endcap)
1892 {
1893 case PS_ENDCAP_SQUARE :
1894 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1895 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1896 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1897 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1898 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1899 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1900 break;
1901 case PS_ENDCAP_FLAT :
1902 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1903 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1904 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1905 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1906 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1907 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1908 break;
1909 case PS_ENDCAP_ROUND :
1910 default :
1911 corners[0].x = xo - penWidthIn;
1912 corners[0].y = yo - penWidthIn;
1913 corners[1].x = xo + penWidthOut;
1914 corners[1].y = yo + penWidthOut;
1915 PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1916 PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1917 PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1918 PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1919 break;
1920 }
1921 }
1922 /* Corpse of the path */
1923 else
1924 {
1925 /* Compute angle */
1926 INT previous, next;
1927 double xa, ya, xb, yb, xo, yo;
1928 double alpha, theta, miterWidth;
1929 DWORD _joint = joint;
1930 POINT pt;
1931 PPATH pInsidePath, pOutsidePath;
1932 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1933 {
1934 previous = j - 1;
1935 next = j + 1;
1936 }
1937 else if (j == 0)
1938 {
1939 previous = pStrokes[i]->numEntriesUsed - 1;
1940 next = j + 1;
1941 }
1942 else
1943 {
1944 previous = j - 1;
1945 next = 0;
1946 }
1947 xo = pStrokes[i]->pPoints[j].x;
1948 yo = pStrokes[i]->pPoints[j].y;
1949 xa = pStrokes[i]->pPoints[previous].x;
1950 ya = pStrokes[i]->pPoints[previous].y;
1951 xb = pStrokes[i]->pPoints[next].x;
1952 yb = pStrokes[i]->pPoints[next].y;
1953 theta = atan2(yo - ya, xo - xa);
1954 alpha = atan2(yb - yo, xb - xo) - theta;
1955 if (alpha > 0) alpha -= M_PI;
1956 else alpha += M_PI;
1957 if (_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha / 2)))
1958 {
1959 _joint = PS_JOIN_BEVEL;
1960 }
1961 if (alpha > 0)
1962 {
1963 pInsidePath = pUpPath;
1964 pOutsidePath = pDownPath;
1965 }
1966 else if (alpha < 0)
1967 {
1968 pInsidePath = pDownPath;
1969 pOutsidePath = pUpPath;
1970 }
1971 else
1972 {
1973 continue;
1974 }
1975 /* Inside angle points */
1976 if (alpha > 0)
1977 {
1978 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1979 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1980 }
1981 else
1982 {
1983 pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
1984 pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
1985 }
1986 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1987 if (alpha > 0)
1988 {
1989 pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
1990 pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
1991 }
1992 else
1993 {
1994 pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
1995 pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
1996 }
1997 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1998 /* Outside angle point */
1999 switch(_joint)
2000 {
2001 case PS_JOIN_MITER :
2002 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2003 pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
2004 pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
2005 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2006 break;
2007 case PS_JOIN_BEVEL :
2008 if (alpha > 0)
2009 {
2010 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2011 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2012 }
2013 else
2014 {
2015 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2016 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2017 }
2018 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2019 if (alpha > 0)
2020 {
2021 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2022 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2023 }
2024 else
2025 {
2026 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2027 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2028 }
2029 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2030 break;
2031 case PS_JOIN_ROUND :
2032 default :
2033 if (alpha > 0)
2034 {
2035 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2036 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2037 }
2038 else
2039 {
2040 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2041 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2042 }
2043 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2044 pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2045 pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2046 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2047 if (alpha > 0)
2048 {
2049 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2050 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2051 }
2052 else
2053 {
2054 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2055 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2056 }
2057 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2058 break;
2059 }
2060 }
2061 }
2062 for (j = 0; j < pUpPath->numEntriesUsed; j++)
2063 {
2064 POINT pt;
2065 pt.x = pUpPath->pPoints[j].x;
2066 pt.y = pUpPath->pPoints[j].y;
2067 PATH_AddEntry(pNewPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
2068 }
2069 for (j = 0; j < pDownPath->numEntriesUsed; j++)
2070 {
2071 POINT pt;
2072 pt.x = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].x;
2073 pt.y = pDownPath->pPoints[pDownPath->numEntriesUsed - j - 1].y;
2074 PATH_AddEntry(pNewPath, &pt, ((j == 0 && (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) ? PT_MOVETO : PT_LINETO));
2075 }
2076
2077 PATH_DestroyGdiPath(pStrokes[i]);
2078 ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2079 PATH_DestroyGdiPath(pUpPath);
2080 ExFreePoolWithTag(pUpPath, TAG_PATH);
2081 PATH_DestroyGdiPath(pDownPath);
2082 ExFreePoolWithTag(pDownPath, TAG_PATH);
2083 }
2084 if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2085
2086 pNewPath->state = PATH_Closed;
2087 if (!(ret = PATH_AssignGdiPath(pPath, pNewPath)))
2088 DPRINT1("Assign path failed\n");
2089 PATH_DestroyGdiPath(pNewPath);
2090 ExFreePoolWithTag(pNewPath, TAG_PATH);
2091 PATH_UnlockPath(pPath);
2092 return ret;
2093 }
2094
2095 static inline INT int_from_fixed(FIXED f)
2096 {
2097 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2098 }
2099
2100 /**********************************************************************
2101 * PATH_BezierTo
2102 *
2103 * Internally used by PATH_add_outline
2104 */
2105 static
2106 VOID
2107 FASTCALL
2108 PATH_BezierTo(
2109 PPATH pPath,
2110 POINT *lppt,
2111 INT n)
2112 {
2113 if (n < 2) return;
2114
2115 if (n == 2)
2116 {
2117 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2118 }
2119 else if (n == 3)
2120 {
2121 PATH_AddEntry(pPath, &lppt[0], PT_BEZIERTO);
2122 PATH_AddEntry(pPath, &lppt[1], PT_BEZIERTO);
2123 PATH_AddEntry(pPath, &lppt[2], PT_BEZIERTO);
2124 }
2125 else
2126 {
2127 POINT pt[3];
2128 INT i = 0;
2129
2130 pt[2] = lppt[0];
2131 n--;
2132
2133 while (n > 2)
2134 {
2135 pt[0] = pt[2];
2136 pt[1] = lppt[i + 1];
2137 pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2138 pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2139 PATH_BezierTo(pPath, pt, 3);
2140 n--;
2141 i++;
2142 }
2143
2144 pt[0] = pt[2];
2145 pt[1] = lppt[i + 1];
2146 pt[2] = lppt[i + 2];
2147 PATH_BezierTo(pPath, pt, 3);
2148 }
2149 }
2150
2151 static
2152 BOOL
2153 FASTCALL
2154 PATH_add_outline(
2155 PDC dc,
2156 INT x,
2157 INT y,
2158 TTPOLYGONHEADER *header,
2159 DWORD size)
2160 {
2161 PPATH pPath;
2162 TTPOLYGONHEADER *start;
2163 POINT pt;
2164 BOOL bResult = FALSE;
2165
2166 start = header;
2167
2168 pPath = PATH_LockPath(dc->dclevel.hPath);
2169 if (!pPath)
2170 {
2171 return FALSE;
2172 }
2173
2174 while ((char *)header < (char *)start + size)
2175 {
2176 TTPOLYCURVE *curve;
2177
2178 if (header->dwType != TT_POLYGON_TYPE)
2179 {
2180 DPRINT1("Unknown header type %lu\n", header->dwType);
2181 goto cleanup;
2182 }
2183
2184 pt.x = x + int_from_fixed(header->pfxStart.x);
2185 pt.y = y - int_from_fixed(header->pfxStart.y);
2186 PATH_AddEntry(pPath, &pt, PT_MOVETO);
2187
2188 curve = (TTPOLYCURVE *)(header + 1);
2189
2190 while ((char *)curve < (char *)header + header->cb)
2191 {
2192 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2193
2194 switch(curve->wType)
2195 {
2196 case TT_PRIM_LINE:
2197 {
2198 WORD i;
2199
2200 for (i = 0; i < curve->cpfx; i++)
2201 {
2202 pt.x = x + int_from_fixed(curve->apfx[i].x);
2203 pt.y = y - int_from_fixed(curve->apfx[i].y);
2204 PATH_AddEntry(pPath, &pt, PT_LINETO);
2205 }
2206 break;
2207 }
2208
2209 case TT_PRIM_QSPLINE:
2210 case TT_PRIM_CSPLINE:
2211 {
2212 WORD i;
2213 POINTFX ptfx;
2214 POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2215
2216 if (!pts) goto cleanup;
2217
2218 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2219
2220 pts[0].x = x + int_from_fixed(ptfx.x);
2221 pts[0].y = y - int_from_fixed(ptfx.y);
2222
2223 for (i = 0; i < curve->cpfx; i++)
2224 {
2225 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2226 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2227 }
2228
2229 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2230
2231 ExFreePoolWithTag(pts, TAG_PATH);
2232 break;
2233 }
2234
2235 default:
2236 DPRINT1("Unknown curve type %04x\n", curve->wType);
2237 goto cleanup;
2238 }
2239
2240 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2241 }
2242 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2243 }
2244
2245 bResult = TRUE;
2246
2247 cleanup:
2248 IntGdiCloseFigure(pPath);
2249 PATH_UnlockPath(pPath);
2250 return bResult;
2251 }
2252
2253 /**********************************************************************
2254 * PATH_ExtTextOut
2255 */
2256 BOOL
2257 FASTCALL
2258 PATH_ExtTextOut(
2259 PDC dc,
2260 INT x,
2261 INT y,
2262 UINT flags,
2263 const RECTL *lprc,
2264 LPCWSTR str,
2265 UINT count,
2266 const INT *dx)
2267 {
2268 unsigned int idx;
2269 POINT offset = {0, 0};
2270
2271 if (!count) return TRUE;
2272
2273 for (idx = 0; idx < count; idx++)
2274 {
2275 MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2276 GLYPHMETRICS gm;
2277 DWORD dwSize;
2278 void *outline;
2279
2280 dwSize = ftGdiGetGlyphOutline(dc,
2281 str[idx],
2282 GGO_GLYPH_INDEX | GGO_NATIVE,
2283 &gm,
2284 0,
2285 NULL,
2286 &identity,
2287 TRUE);
2288 if (dwSize == GDI_ERROR) return FALSE;
2289
2290 /* Add outline only if char is printable */
2291 if (dwSize)
2292 {
2293 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2294 if (!outline) return FALSE;
2295
2296 ftGdiGetGlyphOutline(dc,
2297 str[idx],
2298 GGO_GLYPH_INDEX | GGO_NATIVE,
2299 &gm,
2300 dwSize,
2301 outline,
2302 &identity,
2303 TRUE);
2304
2305 PATH_add_outline(dc, x + offset.x, y + offset.y, outline, dwSize);
2306
2307 ExFreePoolWithTag(outline, TAG_PATH);
2308 }
2309
2310 if (dx)
2311 {
2312 if (flags & ETO_PDY)
2313 {
2314 offset.x += dx[idx * 2];
2315 offset.y += dx[idx * 2 + 1];
2316 }
2317 else
2318 offset.x += dx[idx];
2319 }
2320 else
2321 {
2322 offset.x += gm.gmCellIncX;
2323 offset.y += gm.gmCellIncY;
2324 }
2325 }
2326 return TRUE;
2327 }
2328
2329
2330 /***********************************************************************
2331 * Exported functions
2332 */
2333
2334 BOOL
2335 APIENTRY
2336 NtGdiAbortPath(HDC hDC)
2337 {
2338 PPATH pPath;
2339 PDC dc = DC_LockDc(hDC);
2340 if (!dc)
2341 {
2342 EngSetLastError(ERROR_INVALID_HANDLE);
2343 return FALSE;
2344 }
2345
2346 pPath = PATH_LockPath(dc->dclevel.hPath);
2347 if (!pPath)
2348 {
2349 DC_UnlockDc(dc);
2350 return FALSE;
2351 }
2352
2353 PATH_EmptyPath(pPath);
2354
2355 PATH_UnlockPath(pPath);
2356 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2357
2358 DC_UnlockDc(dc);
2359 return TRUE;
2360 }
2361
2362 BOOL
2363 APIENTRY
2364 NtGdiBeginPath(HDC hDC)
2365 {
2366 PPATH pPath;
2367 PDC dc;
2368
2369 dc = DC_LockDc(hDC);
2370 if (!dc)
2371 {
2372 EngSetLastError(ERROR_INVALID_HANDLE);
2373 return FALSE;
2374 }
2375
2376 /* If path is already open, do nothing. Check if not Save DC state */
2377 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2378 {
2379 DC_UnlockDc(dc);
2380 return TRUE;
2381 }
2382
2383 if (dc->dclevel.hPath)
2384 {
2385 DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2386 if (!(dc->dclevel.flPath & DCPATH_SAVE))
2387 {
2388 // Remove previous handle.
2389 if (!PATH_Delete(dc->dclevel.hPath))
2390 {
2391 DC_UnlockDc(dc);
2392 return FALSE;
2393 }
2394 }
2395 else
2396 {
2397 // Clear flags and Handle.
2398 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2399 dc->dclevel.hPath = NULL;
2400 }
2401 }
2402 pPath = PATH_AllocPathWithHandle();
2403 if (!pPath)
2404 {
2405 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2406 return FALSE;
2407 }
2408 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2409
2410 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2411
2412 DPRINT("BeginPath 2 h 0x%p p 0x%p\n", dc->dclevel.hPath, pPath);
2413 // Path handles are shared. Also due to recursion with in the same thread.
2414 GDIOBJ_vUnlockObject((POBJ)pPath); // Unlock
2415 pPath = PATH_LockPath(dc->dclevel.hPath); // Share Lock.
2416
2417 /* Make sure that path is empty */
2418 PATH_EmptyPath(pPath);
2419
2420 pPath->numEntriesAllocated = NUM_ENTRIES_INITIAL;
2421
2422 pPath->pPoints = (POINT *)ExAllocatePoolWithTag(PagedPool, NUM_ENTRIES_INITIAL * sizeof(POINT), TAG_PATH);
2423 pPath->pFlags = (BYTE *)ExAllocatePoolWithTag(PagedPool, NUM_ENTRIES_INITIAL * sizeof(BYTE), TAG_PATH);
2424
2425 /* Initialize variables for new path */
2426 pPath->newStroke = TRUE;
2427 pPath->state = PATH_Open;
2428
2429 PATH_UnlockPath(pPath);
2430 DC_UnlockDc(dc);
2431 return TRUE;
2432 }
2433
2434 BOOL
2435 APIENTRY
2436 NtGdiCloseFigure(HDC hDC)
2437 {
2438 BOOL Ret = FALSE; // Default to failure
2439 PDC pDc;
2440 PPATH pPath;
2441
2442 DPRINT("Enter %s\n", __FUNCTION__);
2443
2444 pDc = DC_LockDc(hDC);
2445 if (!pDc)
2446 {
2447 EngSetLastError(ERROR_INVALID_PARAMETER);
2448 return FALSE;
2449 }
2450
2451 pPath = PATH_LockPath(pDc->dclevel.hPath);
2452 if (!pPath)
2453 {
2454 DC_UnlockDc(pDc);
2455 return FALSE;
2456 }
2457
2458 if (pPath->state == PATH_Open)
2459 {
2460 IntGdiCloseFigure(pPath);
2461 Ret = TRUE;
2462 }
2463 else
2464 {
2465 // FIXME: Check if lasterror is set correctly
2466 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2467 }
2468
2469 PATH_UnlockPath(pPath);
2470 DC_UnlockDc(pDc);
2471 return Ret;
2472 }
2473
2474 BOOL
2475 APIENTRY
2476 NtGdiEndPath(HDC hDC)
2477 {
2478 BOOL ret = TRUE;
2479 PPATH pPath;
2480 PDC dc;
2481
2482 dc = DC_LockDc(hDC);
2483 if (!dc)
2484 {
2485 EngSetLastError(ERROR_INVALID_HANDLE);
2486 return FALSE;
2487 }
2488
2489 pPath = PATH_LockPath(dc->dclevel.hPath);
2490 if (!pPath)
2491 {
2492 DC_UnlockDc(dc);
2493 return FALSE;
2494 }
2495
2496 /* Check that path is currently being constructed */
2497 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2498 {
2499 DPRINT1("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2500 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2501 ret = FALSE;
2502 }
2503 /* Set flag to indicate that path is finished */
2504 else
2505 {
2506 DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2507 pPath->state = PATH_Closed;
2508 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2509 }
2510
2511 PATH_UnlockPath(pPath);
2512 DC_UnlockDc(dc);
2513 return ret;
2514 }
2515
2516 BOOL
2517 APIENTRY
2518 NtGdiFillPath(HDC hDC)
2519 {
2520 BOOL ret = FALSE;
2521 PPATH pPath;
2522 PDC_ATTR pdcattr;
2523 PDC dc;
2524
2525 dc = DC_LockDc(hDC);
2526 if (!dc)
2527 {
2528 EngSetLastError(ERROR_INVALID_PARAMETER);
2529 return FALSE;
2530 }
2531
2532 pPath = PATH_LockPath(dc->dclevel.hPath);
2533 if (!pPath)
2534 {
2535 DC_UnlockDc(dc);
2536 return FALSE;
2537 }
2538
2539 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2540
2541 pdcattr = dc->pdcattr;
2542
2543 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2544 DC_vUpdateLineBrush(dc);
2545
2546 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2547 DC_vUpdateFillBrush(dc);
2548
2549 ret = PATH_FillPath(dc, pPath);
2550 if (ret)
2551 {
2552 /* FIXME: Should the path be emptied even if conversion
2553 failed? */
2554 PATH_EmptyPath(pPath);
2555 }
2556
2557 PATH_UnlockPath(pPath);
2558 DC_vFinishBlit(dc, NULL);
2559 DC_UnlockDc(dc);
2560 return ret;
2561 }
2562
2563 BOOL
2564 APIENTRY
2565 NtGdiFlattenPath(HDC hDC)
2566 {
2567 BOOL Ret = FALSE;
2568 DC *pDc;
2569 PPATH pPath;
2570
2571 DPRINT("Enter %s\n", __FUNCTION__);
2572
2573 pDc = DC_LockDc(hDC);
2574 if (!pDc)
2575 {
2576 EngSetLastError(ERROR_INVALID_HANDLE);
2577 return FALSE;
2578 }
2579
2580 pPath = PATH_LockPath(pDc->dclevel.hPath);
2581 if (!pPath)
2582 {
2583 DC_UnlockDc(pDc);
2584 return FALSE;
2585 }
2586 if (pPath->state == PATH_Open)
2587 Ret = PATH_FlattenPath(pPath);
2588
2589 PATH_UnlockPath(pPath);
2590 DC_UnlockDc(pDc);
2591 return Ret;
2592 }
2593
2594 _Success_(return != FALSE)
2595 BOOL
2596 APIENTRY
2597 NtGdiGetMiterLimit(
2598 _In_ HDC hdc,
2599 _Out_ PDWORD pdwOut)
2600 {
2601 DC *pDc;
2602 BOOL bResult = TRUE;
2603
2604 if (!(pDc = DC_LockDc(hdc)))
2605 {
2606 EngSetLastError(ERROR_INVALID_PARAMETER);
2607 return FALSE;
2608 }
2609
2610 _SEH2_TRY
2611 {
2612 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2613 *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2614 }
2615 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2616 {
2617 SetLastNtError(_SEH2_GetExceptionCode());
2618 bResult = FALSE;
2619 }
2620 _SEH2_END;
2621
2622 DC_UnlockDc(pDc);
2623 return bResult;
2624
2625 }
2626
2627 INT
2628 APIENTRY
2629 NtGdiGetPath(
2630 HDC hDC,
2631 LPPOINT Points,
2632 LPBYTE Types,
2633 INT nSize)
2634 {
2635 INT ret = -1;
2636 PPATH pPath;
2637
2638 DC *dc = DC_LockDc(hDC);
2639 if (!dc)
2640 {
2641 DPRINT1("Can't lock dc!\n");
2642 EngSetLastError(ERROR_INVALID_PARAMETER);
2643 return -1;
2644 }
2645
2646 pPath = PATH_LockPath(dc->dclevel.hPath);
2647 if (!pPath)
2648 {
2649 DC_UnlockDc(dc);
2650 return -1;
2651 }
2652
2653 if (pPath->state != PATH_Closed)
2654 {
2655 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2656 goto done;
2657 }
2658
2659 if (nSize == 0)
2660 {
2661 ret = pPath->numEntriesUsed;
2662 }
2663 else if (nSize < pPath->numEntriesUsed)
2664 {
2665 EngSetLastError(ERROR_INVALID_PARAMETER);
2666 goto done;
2667 }
2668 else
2669 {
2670 _SEH2_TRY
2671 {
2672 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2673 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2674
2675 /* Convert the points to logical coordinates */
2676 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2677 {
2678 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2679 _SEH2_LEAVE;
2680 }
2681
2682 ret = pPath->numEntriesUsed;
2683 }
2684 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2685 {
2686 SetLastNtError(_SEH2_GetExceptionCode());
2687 }
2688 _SEH2_END
2689 }
2690
2691 done:
2692 PATH_UnlockPath(pPath);
2693 DC_UnlockDc(dc);
2694 return ret;
2695 }
2696
2697 HRGN
2698 APIENTRY
2699 NtGdiPathToRegion(HDC hDC)
2700 {
2701 PPATH pPath;
2702 HRGN hrgnRval = 0;
2703 PREGION Rgn;
2704 DC *pDc;
2705 PDC_ATTR pdcattr;
2706
2707 DPRINT("Enter %s\n", __FUNCTION__);
2708
2709 pDc = DC_LockDc(hDC);
2710 if (!pDc)
2711 {
2712 DPRINT1("Failed to lock DC %p\n", hDC);
2713 EngSetLastError(ERROR_INVALID_PARAMETER);
2714 return NULL;
2715 }
2716
2717 pdcattr = pDc->pdcattr;
2718
2719 pPath = PATH_LockPath(pDc->dclevel.hPath);
2720 if (!pPath)
2721 {
2722 DPRINT1("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2723 DC_UnlockDc(pDc);
2724 return NULL;
2725 }
2726
2727 if (pPath->state != PATH_Closed)
2728 {
2729 // FIXME: Check that setlasterror is being called correctly
2730 DPRINT1("Path is not closed!\n");
2731 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2732 }
2733 else
2734 {
2735 /* Create the region and fill it with the path strokes */
2736 Rgn = REGION_AllocUserRgnWithHandle(1);
2737 if (!Rgn)
2738 {
2739 DPRINT1("Failed to allocate a region\n");
2740 PATH_UnlockPath(pPath);
2741 DC_UnlockDc(pDc);
2742 return NULL;
2743 }
2744 hrgnRval = Rgn->BaseObject.hHmgr;
2745 /* FIXME: Should we empty the path even if conversion failed? */
2746 if (PATH_PathToRegion(pPath, pdcattr->jFillMode, Rgn))
2747 {
2748 PATH_EmptyPath(pPath);
2749 REGION_UnlockRgn(Rgn);
2750 }
2751 else
2752 {
2753 DPRINT1("PATH_PathToRegion failed\n");
2754 REGION_Delete(Rgn);
2755 hrgnRval = NULL;
2756 }
2757 }
2758
2759 PATH_UnlockPath(pPath);
2760 DC_UnlockDc(pDc);
2761 return hrgnRval;
2762 }
2763
2764 BOOL
2765 APIENTRY
2766 NtGdiSetMiterLimit(
2767 IN HDC hdc,
2768 IN DWORD dwNew,
2769 IN OUT OPTIONAL PDWORD pdwOut)
2770 {
2771 DC *pDc;
2772 gxf_long worker, worker1;
2773 BOOL bResult = TRUE;
2774
2775 if (!(pDc = DC_LockDc(hdc)))
2776 {
2777 EngSetLastError(ERROR_INVALID_PARAMETER);
2778 return FALSE;
2779 }
2780
2781 worker.l = dwNew;
2782 worker1.f = pDc->dclevel.laPath.eMiterLimit;
2783 pDc->dclevel.laPath.eMiterLimit = worker.f;
2784
2785 if (pdwOut)
2786 {
2787 _SEH2_TRY
2788 {
2789 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2790 *pdwOut = worker1.l;
2791 }
2792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2793 {
2794 SetLastNtError(_SEH2_GetExceptionCode());
2795 bResult = FALSE;
2796 }
2797 _SEH2_END;
2798 }
2799
2800 DC_UnlockDc(pDc);
2801 return bResult;
2802 }
2803
2804 BOOL
2805 APIENTRY
2806 NtGdiStrokeAndFillPath(HDC hDC)
2807 {
2808 DC *pDc;
2809 PDC_ATTR pdcattr;
2810 PPATH pPath;
2811 BOOL bRet = FALSE;
2812
2813 DPRINT1("Enter %s\n", __FUNCTION__);
2814
2815 if (!(pDc = DC_LockDc(hDC)))
2816 {
2817 EngSetLastError(ERROR_INVALID_PARAMETER);
2818 return FALSE;
2819 }
2820 pPath = PATH_LockPath(pDc->dclevel.hPath);
2821 if (!pPath)
2822 {
2823 DC_UnlockDc(pDc);
2824 return FALSE;
2825 }
2826
2827 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2828
2829 pdcattr = pDc->pdcattr;
2830
2831 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2832 DC_vUpdateFillBrush(pDc);
2833
2834 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2835 DC_vUpdateLineBrush(pDc);
2836
2837 bRet = PATH_FillPath(pDc, pPath);
2838 if (bRet) bRet = PATH_StrokePath(pDc, pPath);
2839 if (bRet) PATH_EmptyPath(pPath);
2840
2841 PATH_UnlockPath(pPath);
2842 DC_vFinishBlit(pDc, NULL);
2843 DC_UnlockDc(pDc);
2844 return bRet;
2845 }
2846
2847 BOOL
2848 APIENTRY
2849 NtGdiStrokePath(HDC hDC)
2850 {
2851 DC *pDc;
2852 PDC_ATTR pdcattr;
2853 PPATH pPath;
2854 BOOL bRet = FALSE;
2855
2856 DPRINT("Enter %s\n", __FUNCTION__);
2857
2858 if (!(pDc = DC_LockDc(hDC)))
2859 {
2860 EngSetLastError(ERROR_INVALID_PARAMETER);
2861 return FALSE;
2862 }
2863
2864 pPath = PATH_LockPath(pDc->dclevel.hPath);
2865 if (!pPath)
2866 {
2867 DC_UnlockDc(pDc);
2868 return FALSE;
2869 }
2870
2871 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2872
2873 pdcattr = pDc->pdcattr;
2874
2875 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2876 DC_vUpdateLineBrush(pDc);
2877
2878 bRet = PATH_StrokePath(pDc, pPath);
2879
2880 DC_vFinishBlit(pDc, NULL);
2881 PATH_EmptyPath(pPath);
2882
2883 PATH_UnlockPath(pPath);
2884 DC_UnlockDc(pDc);
2885 return bRet;
2886 }
2887
2888 BOOL
2889 APIENTRY
2890 NtGdiWidenPath(HDC hDC)
2891 {
2892 BOOL Ret;
2893 PDC pdc = DC_LockDc(hDC);
2894 if (!pdc)
2895 {
2896 EngSetLastError(ERROR_INVALID_PARAMETER);
2897 return FALSE;
2898 }
2899
2900 Ret = PATH_WidenPath(pdc);
2901 DC_UnlockDc(pdc);
2902 return Ret;
2903 }
2904
2905 /* EOF */