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