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