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