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