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