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