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