2 * PROJECT: ReactOS win32 kernel mode subsystem
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: win32ss/gdi/ntgdi/path.c
5 * PURPOSE: Graphics paths (BeginPath, EndPath etc.)
6 * PROGRAMMER: Copyright 1997, 1998 Martin Boehme
8 * 2005 Dmitry Timoshkov
18 #pragma warning(disable:4244)
21 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
23 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
24 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
27 static int PathCount
= 0;
30 /***********************************************************************
35 PATH_CreatePath(int count
)
37 PPATH pPath
= PATH_AllocPathWithHandle();
41 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
45 DPRINT("CreatePath p 0x%p\n", pPath
);
46 // Path handles are shared. Also due to recursion with in the same thread.
47 GDIOBJ_vUnlockObject((POBJ
)pPath
); // Unlock
48 pPath
= PATH_LockPath(pPath
->BaseObject
.hHmgr
); // Share Lock.
50 /* Make sure that path is empty */
51 PATH_EmptyPath(pPath
);
53 count
= max( NUM_ENTRIES_INITIAL
, count
);
55 pPath
->numEntriesAllocated
= count
;
57 pPath
->pPoints
= (POINT
*)ExAllocatePoolWithTag(PagedPool
, count
* sizeof(POINT
), TAG_PATH
);
58 RtlZeroMemory( pPath
->pPoints
, count
* sizeof(POINT
));
59 pPath
->pFlags
= (BYTE
*)ExAllocatePoolWithTag(PagedPool
, count
* sizeof(BYTE
), TAG_PATH
);
60 RtlZeroMemory( pPath
->pFlags
, count
* sizeof(BYTE
));
62 /* Initialize variables for new path */
63 pPath
->numEntriesUsed
= 0;
64 pPath
->newStroke
= TRUE
;
65 pPath
->state
= PATH_Open
;
66 pPath
->pos
.x
= pPath
->pos
.y
= 0;
69 DPRINT("Create Path %d\n",PathCount
);
74 /* PATH_DestroyGdiPath
76 * Destroys a GdiPath structure (frees the memory in the arrays).
80 PATH_DestroyGdiPath(PPATH pPath
)
82 ASSERT(pPath
!= NULL
);
84 if (pPath
->pPoints
) ExFreePoolWithTag(pPath
->pPoints
, TAG_PATH
);
85 if (pPath
->pFlags
) ExFreePoolWithTag(pPath
->pFlags
, TAG_PATH
);
90 PATH_Delete(HPATH hPath
)
93 if (!hPath
) return FALSE
;
94 pPath
= PATH_LockPath(hPath
);
95 if (!pPath
) return FALSE
;
96 PATH_DestroyGdiPath(pPath
);
97 GDIOBJ_vDeleteObject(&pPath
->BaseObject
);
100 DPRINT("Delete Path %d\n",PathCount
);
108 IntGdiCloseFigure(PPATH pPath
)
110 ASSERT(pPath
->state
== PATH_Open
);
112 // FIXME: Shouldn't we draw a line to the beginning of the figure?
113 // Set PT_CLOSEFIGURE on the last entry and start a new stroke
114 if (pPath
->numEntriesUsed
)
116 pPath
->pFlags
[pPath
->numEntriesUsed
- 1] |= PT_CLOSEFIGURE
;
117 pPath
->newStroke
= TRUE
;
121 /* MSDN: This fails if the device coordinates exceed 27 bits, or if the converted
122 logical coordinates exceed 32 bits. */
132 XFORMOBJ_vInit(&xo
, &pdc
->pdcattr
->mxDeviceToWorld
);
133 return XFORMOBJ_bApplyXform(&xo
, XF_LTOL
, count
, (PPOINTL
)ppt
, (PPOINTL
)ppt
);
138 * Initializes the GdiPath structure.
145 ASSERT(pPath
!= NULL
);
147 pPath
->state
= PATH_Null
;
148 pPath
->pPoints
= NULL
;
149 pPath
->pFlags
= NULL
;
150 pPath
->numEntriesUsed
= 0;
151 pPath
->numEntriesAllocated
= 0;
154 /* PATH_AssignGdiPath
156 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
157 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
158 * not just the pointers. Since this means that the arrays in pPathDest may
159 * need to be resized, pPathDest should have been initialized using
160 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
161 * not a copy constructor).
162 * Returns TRUE if successful, else FALSE.
168 const PPATH pPathSrc
)
170 ASSERT(pPathDest
!= NULL
&& pPathSrc
!= NULL
);
172 /* Make sure destination arrays are big enough */
173 if (!PATH_ReserveEntries(pPathDest
, pPathSrc
->numEntriesUsed
))
176 /* Perform the copy operation */
177 memcpy(pPathDest
->pPoints
, pPathSrc
->pPoints
, sizeof(POINT
)*pPathSrc
->numEntriesUsed
);
178 memcpy(pPathDest
->pFlags
, pPathSrc
->pFlags
, sizeof(BYTE
)*pPathSrc
->numEntriesUsed
);
180 pPathDest
->pos
= pPathSrc
->pos
;
181 pPathDest
->state
= pPathSrc
->state
;
182 pPathDest
->numEntriesUsed
= pPathSrc
->numEntriesUsed
;
183 pPathDest
->newStroke
= pPathSrc
->newStroke
;
187 BOOL
PATH_SavePath( DC
*dst
, DC
*src
)
189 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
190 DPRINT("PATH_SavePath\n");
193 DPRINT("PATH_SavePath 1\n");
195 pdstPath
= PATH_CreatePath(psrcPath
->numEntriesAllocated
);
197 dst
->dclevel
.flPath
= src
->dclevel
.flPath
;
199 dst
->dclevel
.hPath
= pdstPath
->BaseObject
.hHmgr
;
201 PATH_AssignGdiPath(pdstPath
, psrcPath
);
203 PATH_UnlockPath(pdstPath
);
204 PATH_UnlockPath(psrcPath
);
209 BOOL
PATH_RestorePath( DC
*dst
, DC
*src
)
211 DPRINT("PATH_RestorePath\n");
213 if (dst
->dclevel
.hPath
== NULL
)
215 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
216 DPRINT("PATH_RestorePath 1\n");
217 pdstPath
= PATH_CreatePath(psrcPath
->numEntriesAllocated
);
218 dst
->dclevel
.flPath
= src
->dclevel
.flPath
;
219 dst
->dclevel
.hPath
= pdstPath
->BaseObject
.hHmgr
;
221 PATH_AssignGdiPath(pdstPath
, psrcPath
);
223 PATH_UnlockPath(pdstPath
);
224 PATH_UnlockPath(psrcPath
);
228 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
229 pdstPath
= PATH_LockPath(dst
->dclevel
.hPath
);
230 DPRINT("PATH_RestorePath 2\n");
231 dst
->dclevel
.flPath
= src
->dclevel
.flPath
& (DCPATH_CLOCKWISE
|DCPATH_ACTIVE
);
232 PATH_AssignGdiPath(pdstPath
, psrcPath
);
234 PATH_UnlockPath(pdstPath
);
235 PATH_UnlockPath(psrcPath
);
242 * Removes all entries from the path and sets the path state to PATH_Null.
246 PATH_EmptyPath(PPATH pPath
)
248 ASSERT(pPath
!= NULL
);
250 pPath
->state
= PATH_Null
;
251 pPath
->numEntriesUsed
= 0;
256 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
257 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
258 * successful, FALSE otherwise (e.g. if not enough memory was available).
267 ASSERT(pPath
!= NULL
);
269 /* FIXME: If newStroke is true, perhaps we want to check that we're
270 * getting a PT_MOVETO
272 DPRINT("(%d,%d) - %d\n", pPoint
->x
, pPoint
->y
, flags
);
274 /* Reserve enough memory for an extra path entry */
275 if (!PATH_ReserveEntries(pPath
, pPath
->numEntriesUsed
+ 1))
278 /* Store information in path entry */
279 pPath
->pPoints
[pPath
->numEntriesUsed
] = *pPoint
;
280 pPath
->pFlags
[pPath
->numEntriesUsed
] = flags
;
282 /* Increment entry count */
283 pPath
->numEntriesUsed
++;
288 /* PATH_ReserveEntries
290 * Ensures that at least "numEntries" entries (for points and flags) have
291 * been allocated; allocates larger arrays and copies the existing entries
292 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
300 INT numEntriesToAllocate
;
304 ASSERT(pPath
!= NULL
);
305 ASSERT(numEntries
>= 0);
307 /* Do we have to allocate more memory? */
308 if (numEntries
> pPath
->numEntriesAllocated
)
310 /* Find number of entries to allocate. We let the size of the array
311 * grow exponentially, since that will guarantee linear time
313 if (pPath
->numEntriesAllocated
)
315 numEntriesToAllocate
= pPath
->numEntriesAllocated
;
316 while (numEntriesToAllocate
< numEntries
)
317 numEntriesToAllocate
= numEntriesToAllocate
* GROW_FACTOR_NUMER
/ GROW_FACTOR_DENOM
;
320 numEntriesToAllocate
= numEntries
;
322 /* Allocate new arrays */
323 pPointsNew
= (POINT
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(POINT
), TAG_PATH
);
327 pFlagsNew
= (BYTE
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(BYTE
), TAG_PATH
);
330 ExFreePoolWithTag(pPointsNew
, TAG_PATH
);
334 /* Copy old arrays to new arrays and discard old arrays */
337 ASSERT(pPath
->pFlags
);
339 memcpy(pPointsNew
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
340 memcpy(pFlagsNew
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
342 ExFreePoolWithTag(pPath
->pPoints
, TAG_PATH
);
343 ExFreePoolWithTag(pPath
->pFlags
, TAG_PATH
);
346 pPath
->pPoints
= pPointsNew
;
347 pPath
->pFlags
= pFlagsNew
;
348 pPath
->numEntriesAllocated
= numEntriesToAllocate
;
354 /* PATH_ScaleNormalizedPoint
356 * Scales a normalized point (x, y) with respect to the box whose corners are
357 * passed in "corners". The point is stored in "*pPoint". The normalized
358 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
359 * (1.0, 1.0) correspond to corners[1].
363 PATH_ScaleNormalizedPoint(
364 FLOAT_POINT corners
[],
372 pPoint
->x
= GDI_ROUND((double)corners
[0].x
+ (double)(corners
[1].x
- corners
[0].x
) * 0.5 * (x
+ 1.0));
373 pPoint
->y
= GDI_ROUND((double)corners
[0].y
+ (double)(corners
[1].y
- corners
[0].y
) * 0.5 * (y
+ 1.0));
376 /* PATH_NormalizePoint
378 * Normalizes a point with respect to the box whose corners are passed in
379 * corners. The normalized coordinates are stored in *pX and *pY.
384 FLOAT_POINT corners
[],
385 const FLOAT_POINT
*pPoint
,
394 *pX
= (double)(pPoint
->x
- corners
[0].x
) / (double)(corners
[1].x
- corners
[0].x
) * 2.0 - 1.0;
395 *pY
= (double)(pPoint
->y
- corners
[0].y
) / (double)(corners
[1].y
- corners
[0].y
) * 2.0 - 1.0;
400 * Helper function for PATH_RoundRect() and PATH_Rectangle()
412 PDC_ATTR pdcattr
= dc
->pdcattr
;
414 /* Convert points to device coordinates */
419 IntLPtoDP(dc
, corners
, 2);
421 /* Make sure first corner is top left and second corner is bottom right */
422 if (corners
[0].x
> corners
[1].x
)
425 corners
[0].x
= corners
[1].x
;
429 if (corners
[0].y
> corners
[1].y
)
432 corners
[0].y
= corners
[1].y
;
436 /* In GM_COMPATIBLE, don't include bottom and right edges */
437 if (pdcattr
->iGraphicsMode
== GM_COMPATIBLE
)
439 if (corners
[0].x
== corners
[1].x
) return FALSE
;
440 if (corners
[0].y
== corners
[1].y
) return FALSE
;
447 /* add a number of points, converting them to device coords */
448 /* return a pointer to the first type byte so it can be fixed up if necessary */
449 static BYTE
*add_log_points( DC
*dc
, PPATH path
, const POINT
*points
,
450 DWORD count
, BYTE type
)
454 if (!PATH_ReserveEntries( path
, path
->numEntriesUsed
+ count
)) return NULL
;
456 ret
= &path
->pFlags
[path
->numEntriesUsed
];
457 memcpy( &path
->pPoints
[path
->numEntriesUsed
], points
, count
* sizeof(*points
) );
458 IntLPtoDP( dc
, &path
->pPoints
[path
->numEntriesUsed
], count
);
459 memset( ret
, type
, count
);
460 path
->numEntriesUsed
+= count
;
464 /* add a number of points that are already in device coords */
465 /* return a pointer to the first type byte so it can be fixed up if necessary */
466 static BYTE
*add_points( PPATH path
, const POINT
*points
, DWORD count
, BYTE type
)
470 if (!PATH_ReserveEntries( path
, path
->numEntriesUsed
+ count
)) return NULL
;
472 ret
= &path
->pFlags
[path
->numEntriesUsed
];
473 memcpy( &path
->pPoints
[path
->numEntriesUsed
], points
, count
* sizeof(*points
) );
474 memset( ret
, type
, count
);
475 path
->numEntriesUsed
+= count
;
479 /* reverse the order of an array of points */
480 static void reverse_points( POINT
*points
, UINT count
)
483 for (i
= 0; i
< count
/ 2; i
++)
485 POINT pt
= points
[i
];
486 points
[i
] = points
[count
- i
- 1];
487 points
[count
- i
- 1] = pt
;
491 /* start a new path stroke if necessary */
492 static BOOL
start_new_stroke( PPATH path
)
494 if (!path
->newStroke
&& path
->numEntriesUsed
&&
495 !(path
->pFlags
[path
->numEntriesUsed
- 1] & PT_CLOSEFIGURE
) &&
496 path
->pPoints
[path
->numEntriesUsed
- 1].x
== path
->pos
.x
&&
497 path
->pPoints
[path
->numEntriesUsed
- 1].y
== path
->pos
.y
)
500 path
->newStroke
= FALSE
;
501 return add_points( path
, &path
->pos
, 1, PT_MOVETO
) != NULL
;
504 /* set current position to the last point that was added to the path */
505 static void update_current_pos( PPATH path
)
507 assert( path
->numEntriesUsed
);
508 path
->pos
= path
->pPoints
[path
->numEntriesUsed
- 1];
511 /* close the current figure */
512 static void close_figure( PPATH path
)
514 assert( path
->numEntriesUsed
);
515 path
->pFlags
[path
->numEntriesUsed
- 1] |= PT_CLOSEFIGURE
;
518 /* add a number of points, starting a new stroke if necessary */
519 static BOOL
add_log_points_new_stroke( DC
*dc
, PPATH path
, const POINT
*points
,
520 DWORD count
, BYTE type
)
522 if (!start_new_stroke( path
)) return FALSE
;
523 if (!add_log_points( dc
, path
, points
, count
, type
)) return FALSE
;
524 update_current_pos( path
);
526 DPRINT("ALPNS : Pos X %d Y %d\n",path
->pos
.x
, path
->pos
.y
);
527 IntGdiMoveToEx(dc
, path
->pos
.x
, path
->pos
.y
, NULL
);
534 * Should be called when a MoveTo is performed on a DC that has an
535 * open path. This starts a new stroke. Returns TRUE if successful, else
544 if (!pPath
) return FALSE
;
546 // GDI32 : Signal from user space of a change in position.
547 if (dc
->pdcattr
->ulDirty_
& DIRTY_STYLESTATE
)
549 DPRINT("MoveTo has changed\n");
550 pPath
->newStroke
= TRUE
;
551 // Set position and clear the signal flag.
552 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
553 IntLPtoDP( dc
, &pPath
->pos
, 1 );
562 * Should be called when a LineTo is performed on a DC that has an
563 * open path. This adds a PT_LINETO entry to the path (and possibly
564 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
565 * Returns TRUE if successful, else FALSE.
576 POINT point
, pointCurPos
;
578 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
579 if (!pPath
) return FALSE
;
584 // Coalesce a MoveTo point.
585 if ( !PATH_MoveTo(dc
, pPath
) )
587 /* Add a PT_MOVETO if necessary */
588 if (pPath
->newStroke
)
590 DPRINT("Line To : New Stroke\n");
591 pPath
->newStroke
= FALSE
;
592 IntGetCurrentPositionEx(dc
, &pointCurPos
);
593 CoordLPtoDP(dc
, &pointCurPos
);
594 if (!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
596 PATH_UnlockPath(pPath
);
601 Ret
= add_log_points_new_stroke( dc
, pPath
, &point
, 1, PT_LINETO
);
602 PATH_UnlockPath(pPath
);
608 * Should be called when a call to Rectangle is performed on a DC that has
609 * an open path. Returns TRUE if successful, else FALSE.
621 POINT corners
[2], points
[4];
624 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
625 if (!pPath
) return FALSE
;
627 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
629 PATH_UnlockPath(pPath
);
633 points
[0].x
= corners
[1].x
;
634 points
[0].y
= corners
[0].y
;
635 points
[1] = corners
[0];
636 points
[2].x
= corners
[0].x
;
637 points
[2].y
= corners
[1].y
;
638 points
[3] = corners
[1];
640 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 4 );
642 if (!(type
= add_points( pPath
, points
, 4, PT_LINETO
)))
644 PATH_UnlockPath(pPath
);
649 /* Close the rectangle figure */
650 IntGdiCloseFigure(pPath
) ;
651 PATH_UnlockPath(pPath
);
657 * Should be called when a call to RoundRect is performed on a DC that has
658 * an open path. Returns TRUE if successful, else FALSE.
672 const double factor
= 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
674 POINT corners
[2], ellipse
[2], points
[16];
676 double width
, height
;
678 if (!ell_width
|| !ell_height
) return PATH_Rectangle( dc
, x1
, y1
, x2
, y2
);
680 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
681 if (!pPath
) return FALSE
;
683 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
685 PATH_UnlockPath(pPath
);
689 ellipse
[0].x
= ellipse
[0].y
= 0;
690 ellipse
[1].x
= ell_width
;
691 ellipse
[1].y
= ell_height
;
692 IntLPtoDP( dc
, &ellipse
, 2 );
693 ell_width
= min( abs( ellipse
[1].x
- ellipse
[0].x
), corners
[1].x
- corners
[0].x
);
694 ell_height
= min( abs( ellipse
[1].y
- ellipse
[0].y
), corners
[1].y
- corners
[0].y
);
695 width
= ell_width
/ 2.0;
696 height
= ell_height
/ 2.0;
699 points
[0].x
= corners
[1].x
;
700 points
[0].y
= corners
[0].y
+ GDI_ROUND( height
);
702 points
[1].x
= corners
[1].x
;
703 points
[1].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
704 points
[2].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
705 points
[2].y
= corners
[0].y
;
706 points
[3].x
= corners
[1].x
- GDI_ROUND( width
);
707 points
[3].y
= corners
[0].y
;
708 /* horizontal line */
709 points
[4].x
= corners
[0].x
+ GDI_ROUND( width
);
710 points
[4].y
= corners
[0].y
;
712 points
[5].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
713 points
[5].y
= corners
[0].y
;
714 points
[6].x
= corners
[0].x
;
715 points
[6].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
716 points
[7].x
= corners
[0].x
;
717 points
[7].y
= corners
[0].y
+ GDI_ROUND( height
);
719 points
[8].x
= corners
[0].x
;
720 points
[8].y
= corners
[1].y
- GDI_ROUND( height
);
722 points
[9].x
= corners
[0].x
;
723 points
[9].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
724 points
[10].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
725 points
[10].y
= corners
[1].y
;
726 points
[11].x
= corners
[0].x
+ GDI_ROUND( width
);
727 points
[11].y
= corners
[1].y
;
728 /* horizontal line */
729 points
[12].x
= corners
[1].x
- GDI_ROUND( width
);
730 points
[12].y
= corners
[1].y
;
732 points
[13].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
733 points
[13].y
= corners
[1].y
;
734 points
[14].x
= corners
[1].x
;
735 points
[14].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
736 points
[15].x
= corners
[1].x
;
737 points
[15].y
= corners
[1].y
- GDI_ROUND( height
);
739 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 16 );
740 if (!(type
= add_points( pPath
, points
, 16, PT_BEZIERTO
)))
742 PATH_UnlockPath(pPath
);
746 type
[4] = type
[8] = type
[12] = PT_LINETO
;
748 IntGdiCloseFigure(pPath
);
749 PATH_UnlockPath(pPath
);
765 const double factor
= 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
767 POINT corners
[2], points
[13];
769 double width
, height
;
771 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
772 if (!pPath
) return FALSE
;
774 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
776 PATH_UnlockPath(pPath
);
780 width
= (corners
[1].x
- corners
[0].x
) / 2.0;
781 height
= (corners
[1].y
- corners
[0].y
) / 2.0;
784 points
[0].x
= corners
[1].x
;
785 points
[0].y
= corners
[0].y
+ GDI_ROUND( height
);
787 points
[1].x
= corners
[1].x
;
788 points
[1].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
789 points
[2].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
790 points
[2].y
= corners
[0].y
;
791 points
[3].x
= corners
[0].x
+ GDI_ROUND( width
);
792 points
[3].y
= corners
[0].y
;
794 points
[4].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
795 points
[4].y
= corners
[0].y
;
796 points
[5].x
= corners
[0].x
;
797 points
[5].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
798 points
[6].x
= corners
[0].x
;
799 points
[6].y
= corners
[0].y
+ GDI_ROUND( height
);
801 points
[7].x
= corners
[0].x
;
802 points
[7].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
803 points
[8].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
804 points
[8].y
= corners
[1].y
;
805 points
[9].x
= corners
[0].x
+ GDI_ROUND( width
);
806 points
[9].y
= corners
[1].y
;
808 points
[10].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
809 points
[10].y
= corners
[1].y
;
810 points
[11].x
= corners
[1].x
;
811 points
[11].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
812 points
[12].x
= corners
[1].x
;
813 points
[12].y
= corners
[1].y
- GDI_ROUND( height
);
815 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 13 );
816 if (!(type
= add_points( pPath
, points
, 13, PT_BEZIERTO
)))
818 DPRINT1("PATH_Ellipse No add\n");
819 PATH_UnlockPath(pPath
);
824 IntGdiCloseFigure(pPath
);
825 PATH_UnlockPath(pPath
);
831 * Creates a Bezier spline that corresponds to part of an arc and appends the
832 * corresponding points to the path. The start and end angles are passed in
833 * "angleStart" and "angleEnd"; these angles should span a quarter circle
834 * at most. If "startEntryType" is non-zero, an entry of that type for the first
835 * control point is added to the path; otherwise, it is assumed that the current
836 * position is equal to the first control point.
842 FLOAT_POINT corners
[],
848 double xNorm
[4], yNorm
[4];
853 ASSERT(fabs(angleEnd
- angleStart
) <= M_PI_2
);
855 /* FIXME: Is there an easier way of computing this? */
857 /* Compute control points */
858 halfAngle
= (angleEnd
- angleStart
) / 2.0;
859 if (fabs(halfAngle
) > 1e-8)
861 a
= 4.0 / 3.0 * (1 - cos(halfAngle
)) / sin(halfAngle
);
862 xNorm
[0] = cos(angleStart
);
863 yNorm
[0] = sin(angleStart
);
864 xNorm
[1] = xNorm
[0] - a
* yNorm
[0];
865 yNorm
[1] = yNorm
[0] + a
* xNorm
[0];
866 xNorm
[3] = cos(angleEnd
);
867 yNorm
[3] = sin(angleEnd
);
868 xNorm
[2] = xNorm
[3] + a
* yNorm
[3];
869 yNorm
[2] = yNorm
[3] - a
* xNorm
[3];
872 for (i
= 0; i
< 4; i
++)
874 xNorm
[i
] = cos(angleStart
);
875 yNorm
[i
] = sin(angleStart
);
878 /* Add starting point to path if desired */
879 start
= !startEntryType
;
881 /* Add remaining control points */
882 for (i
= start
; i
< 4; i
++) PATH_ScaleNormalizedPoint(corners
, xNorm
[i
], yNorm
[i
], &points
[i
]);
883 if (!(type
= add_points( pPath
, points
+ start
, 4 - start
, PT_BEZIERTO
))) return FALSE
;
884 if (!start
) type
[0] = startEntryType
;
891 * Should be called when a call to Arc is performed on a DC that has
892 * an open path. This adds up to five Bezier splines representing the arc
893 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
894 * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
895 * -1 we add 1 extra line from the current DC position to the starting position
896 * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
914 double angleStart
, angleEnd
, angleStartQuadrant
, angleEndQuadrant
= 0.0;
915 /* Initialize angleEndQuadrant to silence gcc's warning */
917 FLOAT_POINT corners
[2], pointStart
, pointEnd
;
918 POINT centre
, pointCurPos
;
919 BOOL start
, end
, Ret
= TRUE
;
924 /* FIXME: This function should check for all possible error returns */
925 /* FIXME: Do we have to respect newStroke? */
929 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
930 if (!pPath
) return FALSE
;
933 clockwise
= ((direction
== AD_CLOCKWISE
) !=0 );
935 clockwise
= ((dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) != 0);
937 /* Check for zero height / width */
938 /* FIXME: Only in GM_COMPATIBLE? */
939 if (x1
== x2
|| y1
== y2
)
944 /* Convert points to device coordinates */
945 corners
[0].x
= (FLOAT
)x1
;
946 corners
[0].y
= (FLOAT
)y1
;
947 corners
[1].x
= (FLOAT
)x2
;
948 corners
[1].y
= (FLOAT
)y2
;
949 pointStart
.x
= (FLOAT
)xStart
;
950 pointStart
.y
= (FLOAT
)yStart
;
951 pointEnd
.x
= (FLOAT
)xEnd
;
952 pointEnd
.y
= (FLOAT
)yEnd
;
953 INTERNAL_LPTODP_FLOAT(dc
, corners
);
954 INTERNAL_LPTODP_FLOAT(dc
, corners
+ 1);
955 INTERNAL_LPTODP_FLOAT(dc
, &pointStart
);
956 INTERNAL_LPTODP_FLOAT(dc
, &pointEnd
);
958 /* Make sure first corner is top left and second corner is bottom right */
959 if (corners
[0].x
> corners
[1].x
)
962 corners
[0].x
= corners
[1].x
;
965 if (corners
[0].y
> corners
[1].y
)
968 corners
[0].y
= corners
[1].y
;
972 /* Compute start and end angle */
973 PATH_NormalizePoint(corners
, &pointStart
, &x
, &y
);
974 angleStart
= atan2(y
, x
);
975 PATH_NormalizePoint(corners
, &pointEnd
, &x
, &y
);
976 angleEnd
= atan2(y
, x
);
978 /* Make sure the end angle is "on the right side" of the start angle */
981 if (angleEnd
<= angleStart
)
983 angleEnd
+= 2 * M_PI
;
984 ASSERT(angleEnd
>= angleStart
);
989 if (angleEnd
>= angleStart
)
991 angleEnd
-= 2 * M_PI
;
992 ASSERT(angleEnd
<= angleStart
);
996 /* In GM_COMPATIBLE, don't include bottom and right edges */
997 if (dc
->pdcattr
->iGraphicsMode
== GM_COMPATIBLE
)
1003 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1004 if (lines
== GdiTypeArcTo
&& pPath
->newStroke
) // -1
1006 pPath
->newStroke
= FALSE
;
1007 IntGetCurrentPositionEx(dc
, &pointCurPos
);
1008 CoordLPtoDP(dc
, &pointCurPos
);
1009 if (!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
1016 /* Add the arc to the path with one Bezier spline per quadrant that the
1022 /* Determine the start and end angles for this quadrant */
1025 angleStartQuadrant
= angleStart
;
1027 angleEndQuadrant
= (floor(angleStart
/ M_PI_2
) + 1.0) * M_PI_2
;
1029 angleEndQuadrant
= (ceil(angleStart
/ M_PI_2
) - 1.0) * M_PI_2
;
1033 angleStartQuadrant
= angleEndQuadrant
;
1035 angleEndQuadrant
+= M_PI_2
;
1037 angleEndQuadrant
-= M_PI_2
;
1040 /* Have we reached the last part of the arc? */
1041 if ((clockwise
&& angleEnd
< angleEndQuadrant
) ||
1042 (!clockwise
&& angleEnd
> angleEndQuadrant
))
1044 /* Adjust the end angle for this quadrant */
1045 angleEndQuadrant
= angleEnd
;
1049 /* Add the Bezier spline to the path */
1050 PATH_DoArcPart(pPath
,
1054 start
? (lines
== GdiTypeArcTo
? PT_LINETO
: PT_MOVETO
) : FALSE
); // -1
1059 if (lines
== GdiTypeArcTo
)
1061 update_current_pos( pPath
);
1063 else /* chord: close figure. pie: add line and close figure */
1064 if (lines
== GdiTypeChord
) // 1
1066 IntGdiCloseFigure(pPath
);
1068 else if (lines
== GdiTypePie
) // 2
1070 centre
.x
= (corners
[0].x
+ corners
[1].x
) / 2;
1071 centre
.y
= (corners
[0].y
+ corners
[1].y
) / 2;
1072 if (!PATH_AddEntry(pPath
, ¢re
, PT_LINETO
| PT_CLOSEFIGURE
))
1076 PATH_UnlockPath(pPath
);
1094 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1095 if (!pPath
) return FALSE
;
1097 ret
= add_log_points_new_stroke( dc
, pPath
, pts
, cbPoints
, PT_BEZIERTO
);
1099 PATH_UnlockPath(pPath
);
1117 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1118 if (!pPath
) return FALSE
;
1120 type
= add_log_points( dc
, pPath
, pts
, cbPoints
, PT_BEZIERTO
);
1121 if (!type
) return FALSE
;
1123 type
[0] = PT_MOVETO
;
1125 PATH_UnlockPath(pPath
);
1138 POINT orig_pos
, cur_pos
;
1139 ULONG i
, lastmove
= 0;
1141 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1142 if (!pPath
) return FALSE
;
1144 if (pPath
->state
!= PATH_Open
)
1146 PATH_UnlockPath(pPath
);
1150 for (i
= 0; i
< pPath
->numEntriesUsed
; i
++) if (pPath
->pFlags
[i
] == PT_MOVETO
) lastmove
= i
;
1151 orig_pos
= pPath
->pos
;
1153 IntGetCurrentPositionEx(dc
, &cur_pos
);
1155 DPRINT("PPD : Current pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1156 DPRINT("PPD : last %d pos X %d Y %d\n",lastmove
, pPath
->pPoints
[lastmove
].x
, pPath
->pPoints
[lastmove
].y
);
1159 for(i
= 0; i
< cbPoints
; i
++)
1164 pPath
->newStroke
= TRUE
;
1165 pPath
->pos
= pts
[i
];
1166 IntLPtoDP( dc
, &pPath
->pos
, 1);
1167 lastmove
= pPath
->numEntriesUsed
;
1170 case PT_LINETO
| PT_CLOSEFIGURE
:
1171 if (!add_log_points_new_stroke( dc
, pPath
, &pts
[i
], 1, PT_LINETO
))
1173 PATH_UnlockPath(pPath
);
1178 if ((i
+ 2 < cbPoints
) && (types
[i
+ 1] == PT_BEZIERTO
) &&
1179 (types
[i
+ 2] & ~PT_CLOSEFIGURE
) == PT_BEZIERTO
)
1181 if (!add_log_points_new_stroke( dc
, pPath
, &pts
[i
], 3, PT_BEZIERTO
))
1183 PATH_UnlockPath(pPath
);
1191 /* restore original position */
1192 pPath
->pos
= orig_pos
;
1194 DPRINT("PPD Bad : pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1196 IntGdiMoveToEx(dc
, cur_pos
.x
, cur_pos
.y
, NULL
);
1198 PATH_UnlockPath(pPath
);
1202 if (types
[i
] & PT_CLOSEFIGURE
)
1204 close_figure( pPath
);
1205 pPath
->pos
= pPath
->pPoints
[lastmove
];
1206 DPRINT("PPD close : pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1209 PATH_UnlockPath(pPath
);
1227 if (cbPoints
< 1) return FALSE
;
1229 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1230 if (!pPath
) return FALSE
;
1232 ret
= add_log_points_new_stroke( dc
, pPath
, pts
, cbPoints
, PT_LINETO
);
1233 PATH_UnlockPath(pPath
);
1254 if (!polygons
) return FALSE
;
1256 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1257 if (!pPath
) return FALSE
;
1260 for (poly
= count
= 0; poly
< polygons
; poly
++)
1262 if (counts
[poly
] < 2)
1264 PATH_UnlockPath(pPath
);
1267 count
+= counts
[poly
];
1270 type
= add_log_points( dc
, pPath
, pts
, count
, PT_LINETO
);
1273 PATH_UnlockPath(pPath
);
1277 /* make the first point of each polyline a PT_MOVETO, and close the last one */
1278 for (poly
= 0; poly
< polygons
; type
+= counts
[poly
++])
1280 type
[0] = PT_MOVETO
;
1281 type
[counts
[poly
] - 1] = PT_LINETO
| PT_CLOSEFIGURE
;
1283 PATH_UnlockPath(pPath
);
1292 const DWORD
* counts
,
1296 ULONG poly
, point
, i
;
1304 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1310 for (i
= 0, poly
= 0; poly
< polylines
; poly
++)
1312 for (point
= 0; point
< counts
[poly
]; point
++, i
++)
1315 CoordLPtoDP(dc
, &pt
);
1316 PATH_AddEntry(pPath
, &pt
, (point
== 0) ? PT_MOVETO
: PT_LINETO
);
1319 DPRINT("PATH_PolyPolyline end count %d\n",pPath
->numEntriesUsed
);
1320 PATH_UnlockPath(pPath
);
1324 /* PATH_AddFlatBezier
1338 pts
= GDI_Bezier(pt
, 4, &no
);
1339 if (!pts
) return FALSE
;
1341 for (i
= 1; i
< no
; i
++)
1343 if (!(ret
= PATH_AddEntry(pPath
, &pts
[i
], (i
== no
- 1 && closed
) ? PT_LINETO
| PT_CLOSEFIGURE
: PT_LINETO
)))
1347 ExFreePoolWithTag(pts
, TAG_BEZIER
);
1353 * Replaces Beziers with line segments
1358 PATH_FlattenPath(PPATH pPath
)
1362 DPRINT("PATH_FlattenPath\n");
1363 if (!(newPath
= PATH_CreatePath(pPath
->numEntriesUsed
))) return NULL
;
1365 for (srcpt
= 0; srcpt
< pPath
->numEntriesUsed
; srcpt
++)
1367 switch(pPath
->pFlags
[srcpt
] & ~PT_CLOSEFIGURE
)
1371 if (!PATH_AddEntry(newPath
, &pPath
->pPoints
[srcpt
], pPath
->pFlags
[srcpt
]))
1373 PATH_UnlockPath(newPath
);
1374 PATH_Delete(newPath
->BaseObject
.hHmgr
);
1379 if(!PATH_AddFlatBezier(newPath
, &pPath
->pPoints
[srcpt
- 1], pPath
->pFlags
[srcpt
+ 2] & PT_CLOSEFIGURE
))
1381 PATH_UnlockPath(newPath
);
1382 PATH_Delete(newPath
->BaseObject
.hHmgr
);
1389 DPRINT("PATH_FlattenPath good\n");
1390 newPath
->state
= pPath
->state
;
1394 /* PATH_PathToRegion
1396 * Creates a region from the specified path using the specified polygon
1397 * filling mode. The path is left unchanged. A handle to the region that
1398 * was created is stored in *pHrgn.
1407 int i
, pos
, polygons
;
1411 if (!pPath
->numEntriesUsed
) return FALSE
;
1413 counts
= ExAllocatePoolWithTag(PagedPool
, (pPath
->numEntriesUsed
/ 2) * sizeof(counts
), TAG_PATH
);
1416 DPRINT1("Failed to allocate %lu strokes\n", (pPath
->numEntriesUsed
/ 2) * sizeof(*counts
));
1417 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1422 ASSERT( pPath
->pFlags
[0] == PT_MOVETO
);
1423 for (i
= 1; i
< pPath
->numEntriesUsed
; i
++)
1425 if (pPath
->pFlags
[i
] != PT_MOVETO
) continue;
1426 counts
[polygons
++] = i
- pos
;
1429 if (i
> pos
+ 1) counts
[polygons
++] = i
- pos
;
1431 ASSERT( polygons
<= pPath
->numEntriesUsed
/ 2 );
1433 /* Fill the region with the strokes */
1434 Ret
= REGION_SetPolyPolygonRgn(Rgn
,
1441 DPRINT1("REGION_SetPolyPolygonRgn failed\n");
1444 ExFreePoolWithTag(counts
, TAG_PATH
);
1452 * You can play with this as long as you like, but if you break Area.exe the purge will Begain on Path!!!
1461 return PATH_FillPathEx(dc
, pPath
, NULL
);
1471 INT mapMode
, graphicsMode
;
1472 SIZE ptViewportExt
, ptWindowExt
;
1473 POINTL ptViewportOrg
, ptWindowOrg
;
1476 PDC_ATTR pdcattr
= dc
->pdcattr
;
1478 /* Allocate a temporary region */
1479 Rgn
= IntSysCreateRectpRgn(0, 0, 0, 0);
1482 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1486 if (!PATH_PathToRegion(pPath
, pdcattr
->jFillMode
, Rgn
))
1488 DPRINT("PFP : Fail P2R\n");
1489 /* EngSetLastError ? */
1494 /* Since PaintRgn interprets the region as being in logical coordinates
1495 * but the points we store for the path are already in device
1496 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
1497 * Using SaveDC to save information about the mapping mode / world
1498 * transform would be easier but would require more overhead, especially
1499 * now that SaveDC saves the current path.
1502 /* Save the information about the old mapping mode */
1503 mapMode
= pdcattr
->iMapMode
;
1504 ptViewportExt
= pdcattr
->szlViewportExt
;
1505 ptViewportOrg
= pdcattr
->ptlViewportOrg
;
1506 ptWindowExt
= pdcattr
->szlWindowExt
;
1507 ptWindowOrg
= pdcattr
->ptlWindowOrg
;
1509 /* Save world transform
1510 * NB: The Windows documentation on world transforms would lead one to
1511 * believe that this has to be done only in GM_ADVANCED; however, my
1512 * tests show that resetting the graphics mode to GM_COMPATIBLE does
1513 * not reset the world transform.
1515 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1518 IntGdiSetMapMode(dc
, MM_TEXT
);
1519 pdcattr
->ptlViewportOrg
.x
= 0;
1520 pdcattr
->ptlViewportOrg
.y
= 0;
1521 pdcattr
->ptlWindowOrg
.x
= 0;
1522 pdcattr
->ptlWindowOrg
.y
= 0;
1524 graphicsMode
= pdcattr
->iGraphicsMode
;
1525 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1526 GreModifyWorldTransform(dc
, &xform
, MWT_IDENTITY
);
1527 pdcattr
->iGraphicsMode
= graphicsMode
;
1529 /* Paint the region */
1530 IntGdiFillRgn(dc
, Rgn
, pbrFill
);
1532 /* Restore the old mapping mode */
1533 IntGdiSetMapMode(dc
, mapMode
);
1534 pdcattr
->szlViewportExt
= ptViewportExt
;
1535 pdcattr
->ptlViewportOrg
= ptViewportOrg
;
1536 pdcattr
->szlWindowExt
= ptWindowExt
;
1537 pdcattr
->ptlWindowOrg
= ptWindowOrg
;
1539 /* Go to GM_ADVANCED temporarily to restore the world transform */
1540 graphicsMode
= pdcattr
->iGraphicsMode
;
1541 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1542 GreModifyWorldTransform(dc
, &xform
, MWT_SET
);
1543 pdcattr
->iGraphicsMode
= graphicsMode
;
1555 INT nLinePts
, nAlloc
;
1556 POINT
*pLinePts
= NULL
;
1557 POINT ptViewportOrg
, ptWindowOrg
;
1558 SIZE szViewportExt
, szWindowExt
;
1559 DWORD mapMode
, graphicsMode
;
1561 PDC_ATTR pdcattr
= dc
->pdcattr
;
1563 DPRINT("Enter %s\n", __FUNCTION__
);
1565 /* Save the mapping mode info */
1566 mapMode
= pdcattr
->iMapMode
;
1568 szViewportExt
= *DC_pszlViewportExt(dc
);
1569 ptViewportOrg
= dc
->pdcattr
->ptlViewportOrg
;
1570 szWindowExt
= dc
->pdcattr
->szlWindowExt
;
1571 ptWindowOrg
= dc
->pdcattr
->ptlWindowOrg
;
1573 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1576 pdcattr
->iMapMode
= MM_TEXT
;
1577 pdcattr
->ptlViewportOrg
.x
= 0;
1578 pdcattr
->ptlViewportOrg
.y
= 0;
1579 pdcattr
->ptlWindowOrg
.x
= 0;
1580 pdcattr
->ptlWindowOrg
.y
= 0;
1581 graphicsMode
= pdcattr
->iGraphicsMode
;
1582 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1583 GreModifyWorldTransform(dc
, (XFORML
*)&xform
, MWT_IDENTITY
);
1584 pdcattr
->iGraphicsMode
= graphicsMode
;
1586 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1587 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1588 * space in case we get one to keep the number of reallocations small. */
1589 nAlloc
= pPath
->numEntriesUsed
+ 1 + 300;
1590 pLinePts
= ExAllocatePoolWithTag(PagedPool
, nAlloc
* sizeof(POINT
), TAG_PATH
);
1593 DPRINT1("Can't allocate pool!\n");
1594 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1599 for (i
= 0; i
< pPath
->numEntriesUsed
; i
++)
1601 if ((i
== 0 || (pPath
->pFlags
[i
- 1] & PT_CLOSEFIGURE
))
1602 && (pPath
->pFlags
[i
] != PT_MOVETO
))
1604 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1605 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1606 (INT
)pPath
->pFlags
[i
]);
1610 switch(pPath
->pFlags
[i
])
1613 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1614 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1615 if (nLinePts
>= 2) IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1617 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1620 case (PT_LINETO
| PT_CLOSEFIGURE
):
1621 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1622 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1623 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1626 DPRINT("Got PT_BEZIERTO\n");
1627 if (pPath
->pFlags
[i
+ 1] != PT_BEZIERTO
||
1628 (pPath
->pFlags
[i
+ 2] & ~PT_CLOSEFIGURE
) != PT_BEZIERTO
)
1630 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1636 INT nBzrPts
, nMinAlloc
;
1637 POINT
*pBzrPts
= GDI_Bezier(&pPath
->pPoints
[i
- 1], 4, &nBzrPts
);
1638 /* Make sure we have allocated enough memory for the lines of
1639 * this bezier and the rest of the path, assuming we won't get
1640 * another one (since we won't reallocate again then). */
1641 nMinAlloc
= nLinePts
+ (pPath
->numEntriesUsed
- i
) + nBzrPts
;
1642 if (nAlloc
< nMinAlloc
)
1644 // Reallocate memory
1646 POINT
*Realloc
= NULL
;
1647 nAlloc
= nMinAlloc
* 2;
1649 Realloc
= ExAllocatePoolWithTag(PagedPool
,
1650 nAlloc
* sizeof(POINT
),
1655 DPRINT1("Can't allocate pool!\n");
1656 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1660 memcpy(Realloc
, pLinePts
, nLinePts
* sizeof(POINT
));
1661 ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1664 memcpy(&pLinePts
[nLinePts
], &pBzrPts
[1], (nBzrPts
- 1) * sizeof(POINT
));
1665 nLinePts
+= nBzrPts
- 1;
1666 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1671 DPRINT1("Got path flag %d (not supported)\n", (INT
)pPath
->pFlags
[i
]);
1675 if (pPath
->pFlags
[i
] & PT_CLOSEFIGURE
)
1677 pLinePts
[nLinePts
++] = pLinePts
[0];
1681 IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1686 if (pLinePts
) ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1688 /* Restore the old mapping mode */
1689 pdcattr
->iMapMode
= mapMode
;
1690 pdcattr
->szlWindowExt
.cx
= szWindowExt
.cx
;
1691 pdcattr
->szlWindowExt
.cy
= szWindowExt
.cy
;
1692 pdcattr
->ptlWindowOrg
.x
= ptWindowOrg
.x
;
1693 pdcattr
->ptlWindowOrg
.y
= ptWindowOrg
.y
;
1695 pdcattr
->szlViewportExt
.cx
= szViewportExt
.cx
;
1696 pdcattr
->szlViewportExt
.cy
= szViewportExt
.cy
;
1697 pdcattr
->ptlViewportOrg
.x
= ptViewportOrg
.x
;
1698 pdcattr
->ptlViewportOrg
.y
= ptViewportOrg
.y
;
1700 /* Restore the world transform */
1701 XForm2MatrixS(&dc
->pdcattr
->mxWorldToPage
, &xform
);
1703 /* If we've moved the current point then get its new position
1704 which will be in device (MM_TEXT) co-ords, convert it to
1705 logical co-ords and re-set it. This basically updates
1706 dc->CurPosX|Y so that their values are in the correct mapping
1712 IntGetCurrentPositionEx(dc
, &pt
);
1713 IntDPtoLP(dc
, &pt
, 1);
1714 IntGdiMoveToEx(dc
, pt
.x
, pt
.y
, NULL
);
1716 DPRINT("Leave %s, ret=%d\n", __FUNCTION__
, ret
);
1720 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1725 PATH_WidenPath(DC
*dc
)
1727 INT i
, j
, numStrokes
, numOldStrokes
, penWidth
, penWidthIn
, penWidthOut
, size
, penStyle
;
1728 PPATH pPath
, flat_path
, pNewPath
, *pStrokes
= NULL
, *pOldStrokes
, pUpPath
, pDownPath
;
1731 DWORD obj_type
, joint
, endcap
, penType
;
1732 PDC_ATTR pdcattr
= dc
->pdcattr
;
1734 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1737 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
1741 if (pPath
->state
!= PATH_Closed
)
1744 PATH_UnlockPath(pPath
);
1745 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1749 size
= GreGetObject(pdcattr
->hpen
, 0, NULL
);
1753 PATH_UnlockPath(pPath
);
1754 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1758 elp
= ExAllocatePoolWithTag(PagedPool
, size
, TAG_PATH
);
1762 PATH_UnlockPath(pPath
);
1763 EngSetLastError(ERROR_OUTOFMEMORY
);
1767 GreGetObject(pdcattr
->hpen
, size
, elp
);
1769 obj_type
= GDI_HANDLE_GET_TYPE(pdcattr
->hpen
);
1770 if (obj_type
== GDI_OBJECT_TYPE_PEN
)
1772 penStyle
= ((LOGPEN
*)elp
)->lopnStyle
;
1774 else if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
)
1776 penStyle
= elp
->elpPenStyle
;
1781 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1782 ExFreePoolWithTag(elp
, TAG_PATH
);
1783 PATH_UnlockPath(pPath
);
1787 penWidth
= elp
->elpWidth
;
1788 ExFreePoolWithTag(elp
, TAG_PATH
);
1790 endcap
= (PS_ENDCAP_MASK
& penStyle
);
1791 joint
= (PS_JOIN_MASK
& penStyle
);
1792 penType
= (PS_TYPE_MASK
& penStyle
);
1794 /* The function cannot apply to cosmetic pens */
1795 if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
&& penType
== PS_COSMETIC
)
1798 PATH_UnlockPath(pPath
);
1799 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1803 if (!(flat_path
= PATH_FlattenPath(pPath
)))
1805 PATH_UnlockPath(pPath
);
1808 PATH_UnlockPath(pPath
);
1810 penWidthIn
= penWidth
/ 2;
1811 penWidthOut
= penWidth
/ 2;
1812 if (penWidthIn
+ penWidthOut
< penWidth
)
1817 for (i
= 0, j
= 0; i
< flat_path
->numEntriesUsed
; i
++, j
++)
1820 if ((i
== 0 || (flat_path
->pFlags
[i
- 1] & PT_CLOSEFIGURE
)) &&
1821 (flat_path
->pFlags
[i
] != PT_MOVETO
))
1823 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1824 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1825 flat_path
->pFlags
[i
]);
1827 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1828 PATH_UnlockPath(flat_path
);
1829 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1832 switch(flat_path
->pFlags
[i
])
1837 pStrokes
[numStrokes
- 1]->state
= PATH_Closed
;
1839 numOldStrokes
= numStrokes
;
1842 if (numStrokes
== 1)
1843 pStrokes
= ExAllocatePoolWithTag(PagedPool
, sizeof(*pStrokes
), TAG_PATH
);
1846 pOldStrokes
= pStrokes
; // Save old pointer.
1847 pStrokes
= ExAllocatePoolWithTag(PagedPool
, numStrokes
* sizeof(*pStrokes
), TAG_PATH
);
1850 PATH_UnlockPath(flat_path
);
1851 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1854 RtlCopyMemory(pStrokes
, pOldStrokes
, numOldStrokes
* sizeof(PPATH
));
1855 ExFreePoolWithTag(pOldStrokes
, TAG_PATH
); // Free old pointer.
1859 PATH_UnlockPath(flat_path
);
1860 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1863 pStrokes
[numStrokes
- 1] = ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1864 if (!pStrokes
[numStrokes
- 1])
1866 ASSERT(FALSE
); // FIXME
1868 PATH_InitGdiPath(pStrokes
[numStrokes
- 1]);
1869 pStrokes
[numStrokes
- 1]->state
= PATH_Open
;
1871 case (PT_LINETO
| PT_CLOSEFIGURE
):
1872 point
.x
= flat_path
->pPoints
[i
].x
;
1873 point
.y
= flat_path
->pPoints
[i
].y
;
1874 PATH_AddEntry(pStrokes
[numStrokes
- 1], &point
, flat_path
->pFlags
[i
]);
1877 /* Should never happen because of the FlattenPath call */
1878 DPRINT1("Should never happen\n");
1881 DPRINT1("Got path flag %c\n", flat_path
->pFlags
[i
]);
1883 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1884 PATH_UnlockPath(flat_path
);
1885 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1890 pNewPath
= PATH_CreatePath( flat_path
->numEntriesUsed
);
1892 for (i
= 0; i
< numStrokes
; i
++)
1894 pUpPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1895 PATH_InitGdiPath(pUpPath
);
1896 pUpPath
->state
= PATH_Open
;
1897 pDownPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1898 PATH_InitGdiPath(pDownPath
);
1899 pDownPath
->state
= PATH_Open
;
1901 for (j
= 0; j
< pStrokes
[i
]->numEntriesUsed
; j
++)
1903 /* Beginning or end of the path if not closed */
1904 if ((!(pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
)) && (j
== 0 || j
== pStrokes
[i
]->numEntriesUsed
- 1))
1906 /* Compute segment angle */
1907 double xo
, yo
, xa
, ya
, theta
;
1909 FLOAT_POINT corners
[2];
1912 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1913 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1914 xa
= pStrokes
[i
]->pPoints
[1].x
;
1915 ya
= pStrokes
[i
]->pPoints
[1].y
;
1919 xa
= pStrokes
[i
]->pPoints
[j
- 1].x
;
1920 ya
= pStrokes
[i
]->pPoints
[j
- 1].y
;
1921 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1922 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1924 theta
= atan2(ya
- yo
, xa
- xo
);
1927 case PS_ENDCAP_SQUARE
:
1928 pt
.x
= xo
+ round(sqrt(2) * penWidthOut
* cos(M_PI_4
+ theta
));
1929 pt
.y
= yo
+ round(sqrt(2) * penWidthOut
* sin(M_PI_4
+ theta
));
1930 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1931 pt
.x
= xo
+ round(sqrt(2) * penWidthIn
* cos(- M_PI_4
+ theta
));
1932 pt
.y
= yo
+ round(sqrt(2) * penWidthIn
* sin(- M_PI_4
+ theta
));
1933 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1935 case PS_ENDCAP_FLAT
:
1936 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
1937 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
1938 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1939 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
1940 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
1941 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1943 case PS_ENDCAP_ROUND
:
1945 corners
[0].x
= xo
- penWidthIn
;
1946 corners
[0].y
= yo
- penWidthIn
;
1947 corners
[1].x
= xo
+ penWidthOut
;
1948 corners
[1].y
= yo
+ penWidthOut
;
1949 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI_2
, theta
+ 3 * M_PI_4
, (j
== 0 ? PT_MOVETO
: FALSE
));
1950 PATH_DoArcPart(pUpPath
, corners
, theta
+ 3 * M_PI_4
, theta
+ M_PI
, FALSE
);
1951 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI
, theta
+ 5 * M_PI_4
, FALSE
);
1952 PATH_DoArcPart(pUpPath
, corners
, theta
+ 5 * M_PI_4
, theta
+ 3 * M_PI_2
, FALSE
);
1956 /* Corpse of the path */
1961 double xa
, ya
, xb
, yb
, xo
, yo
;
1962 double alpha
, theta
, miterWidth
;
1963 DWORD _joint
= joint
;
1965 PPATH pInsidePath
, pOutsidePath
;
1966 if (j
> 0 && j
< pStrokes
[i
]->numEntriesUsed
- 1)
1973 previous
= pStrokes
[i
]->numEntriesUsed
- 1;
1981 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1982 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1983 xa
= pStrokes
[i
]->pPoints
[previous
].x
;
1984 ya
= pStrokes
[i
]->pPoints
[previous
].y
;
1985 xb
= pStrokes
[i
]->pPoints
[next
].x
;
1986 yb
= pStrokes
[i
]->pPoints
[next
].y
;
1987 theta
= atan2(yo
- ya
, xo
- xa
);
1988 alpha
= atan2(yb
- yo
, xb
- xo
) - theta
;
1989 if (alpha
> 0) alpha
-= M_PI
;
1991 if (_joint
== PS_JOIN_MITER
&& dc
->dclevel
.laPath
.eMiterLimit
< fabs(1 / sin(alpha
/ 2)))
1993 _joint
= PS_JOIN_BEVEL
;
1997 pInsidePath
= pUpPath
;
1998 pOutsidePath
= pDownPath
;
2002 pInsidePath
= pDownPath
;
2003 pOutsidePath
= pUpPath
;
2009 /* Inside angle points */
2012 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
2013 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
2017 pt
.x
= xo
+ round(penWidthIn
* cos(theta
+ M_PI_2
));
2018 pt
.y
= yo
+ round(penWidthIn
* sin(theta
+ M_PI_2
));
2020 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
2023 pt
.x
= xo
+ round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
2024 pt
.y
= yo
+ round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
2028 pt
.x
= xo
- round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
2029 pt
.y
= yo
- round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
2031 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
2032 /* Outside angle point */
2035 case PS_JOIN_MITER
:
2036 miterWidth
= fabs(penWidthOut
/ cos(M_PI_2
- fabs(alpha
) / 2));
2037 pt
.x
= xo
+ round(miterWidth
* cos(theta
+ alpha
/ 2));
2038 pt
.y
= yo
+ round(miterWidth
* sin(theta
+ alpha
/ 2));
2039 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2041 case PS_JOIN_BEVEL
:
2044 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
2045 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
2049 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
2050 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
2052 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2055 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2056 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2060 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2061 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2063 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2065 case PS_JOIN_ROUND
:
2069 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
2070 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
2074 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
2075 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
2077 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2078 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ alpha
/ 2));
2079 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ alpha
/ 2));
2080 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2083 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2084 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2088 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2089 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2091 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2096 type
= add_points( pNewPath
, pUpPath
->pPoints
, pUpPath
->numEntriesUsed
, PT_LINETO
);
2097 type
[0] = PT_MOVETO
;
2098 reverse_points( pDownPath
->pPoints
, pDownPath
->numEntriesUsed
);
2099 type
= add_points( pNewPath
, pDownPath
->pPoints
, pDownPath
->numEntriesUsed
, PT_LINETO
);
2100 if (pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
) type
[0] = PT_MOVETO
;
2102 PATH_DestroyGdiPath(pStrokes
[i
]);
2103 ExFreePoolWithTag(pStrokes
[i
], TAG_PATH
);
2104 PATH_DestroyGdiPath(pUpPath
);
2105 ExFreePoolWithTag(pUpPath
, TAG_PATH
);
2106 PATH_DestroyGdiPath(pDownPath
);
2107 ExFreePoolWithTag(pDownPath
, TAG_PATH
);
2109 if (pStrokes
) ExFreePoolWithTag(pStrokes
, TAG_PATH
);
2111 PATH_UnlockPath(flat_path
);
2112 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
2113 pNewPath
->state
= PATH_Closed
;
2114 PATH_UnlockPath(pNewPath
);
2118 static inline INT
int_from_fixed(FIXED f
)
2120 return (f
.fract
>= 0x8000) ? (f
.value
+ 1) : f
.value
;
2123 /**********************************************************************
2126 * Internally used by PATH_add_outline
2140 PATH_AddEntry(pPath
, &lppt
[1], PT_LINETO
);
2144 add_points( pPath
, lppt
, 3, PT_BEZIERTO
);
2157 pt
[1] = lppt
[i
+ 1];
2158 pt
[2].x
= (lppt
[i
+ 2].x
+ lppt
[i
+ 1].x
) / 2;
2159 pt
[2].y
= (lppt
[i
+ 2].y
+ lppt
[i
+ 1].y
) / 2;
2160 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2166 pt
[1] = lppt
[i
+ 1];
2167 pt
[2] = lppt
[i
+ 2];
2168 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2180 TTPOLYGONHEADER
*header
,
2183 TTPOLYGONHEADER
*start
;
2185 BOOL bResult
= FALSE
;
2189 while ((char *)header
< (char *)start
+ size
)
2193 if (header
->dwType
!= TT_POLYGON_TYPE
)
2195 DPRINT1("Unknown header type %lu\n", header
->dwType
);
2199 pt
.x
= x
+ int_from_fixed(header
->pfxStart
.x
);
2200 pt
.y
= y
- int_from_fixed(header
->pfxStart
.y
);
2201 PATH_AddEntry(pPath
, &pt
, PT_MOVETO
);
2203 curve
= (TTPOLYCURVE
*)(header
+ 1);
2205 while ((char *)curve
< (char *)header
+ header
->cb
)
2207 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2209 switch(curve
->wType
)
2215 for (i
= 0; i
< curve
->cpfx
; i
++)
2217 pt
.x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2218 pt
.y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2219 PATH_AddEntry(pPath
, &pt
, PT_LINETO
);
2224 case TT_PRIM_QSPLINE
:
2225 case TT_PRIM_CSPLINE
:
2229 POINT
*pts
= ExAllocatePoolWithTag(PagedPool
, (curve
->cpfx
+ 1) * sizeof(POINT
), TAG_PATH
);
2231 if (!pts
) goto cleanup
;
2233 ptfx
= *(POINTFX
*)((char *)curve
- sizeof(POINTFX
));
2235 pts
[0].x
= x
+ int_from_fixed(ptfx
.x
);
2236 pts
[0].y
= y
- int_from_fixed(ptfx
.y
);
2238 for (i
= 0; i
< curve
->cpfx
; i
++)
2240 pts
[i
+ 1].x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2241 pts
[i
+ 1].y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2244 PATH_BezierTo(pPath
, pts
, curve
->cpfx
+ 1);
2246 ExFreePoolWithTag(pts
, TAG_PATH
);
2251 DPRINT1("Unknown curve type %04x\n", curve
->wType
);
2255 curve
= (TTPOLYCURVE
*)&curve
->apfx
[curve
->cpfx
];
2257 header
= (TTPOLYGONHEADER
*)((char *)header
+ header
->cb
);
2263 IntGdiCloseFigure(pPath
);
2267 /**********************************************************************
2283 unsigned int idx
, ggo_flags
= GGO_NATIVE
;
2284 POINT offset
= {0, 0};
2286 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2292 if (pPath
->state
!= PATH_Open
)
2294 DPRINT1("PATH_ExtTextOut not open\n");
2298 if (!count
) return TRUE
;
2299 if (flags
& ETO_GLYPH_INDEX
) ggo_flags
|= GGO_GLYPH_INDEX
;
2301 for (idx
= 0; idx
< count
; idx
++)
2303 MAT2 identity
= { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2308 dwSize
= ftGdiGetGlyphOutline(dc
,
2316 if (dwSize
== GDI_ERROR
)
2318 PATH_UnlockPath(pPath
);
2322 /* Add outline only if char is printable */
2325 outline
= ExAllocatePoolWithTag(PagedPool
, dwSize
, TAG_PATH
);
2328 PATH_UnlockPath(pPath
);
2332 ftGdiGetGlyphOutline(dc
,
2341 PATH_add_outline(dc
, pPath
, x
+ offset
.x
, y
+ offset
.y
, outline
, dwSize
);
2343 ExFreePoolWithTag(outline
, TAG_PATH
);
2348 if (flags
& ETO_PDY
)
2350 offset
.x
+= dx
[idx
* 2];
2351 offset
.y
+= dx
[idx
* 2 + 1];
2354 offset
.x
+= dx
[idx
];
2358 offset
.x
+= gm
.gmCellIncX
;
2359 offset
.y
+= gm
.gmCellIncY
;
2362 PATH_UnlockPath(pPath
);
2367 /***********************************************************************
2368 * Exported functions
2373 NtGdiAbortPath(HDC hDC
)
2375 PDC dc
= DC_LockDc(hDC
);
2378 EngSetLastError(ERROR_INVALID_HANDLE
);
2382 if (!dc
->dclevel
.hPath
)
2388 if (!PATH_Delete(dc
->dclevel
.hPath
))
2394 dc
->dclevel
.hPath
= 0;
2395 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2403 NtGdiBeginPath(HDC hDC
)
2408 dc
= DC_LockDc(hDC
);
2411 EngSetLastError(ERROR_INVALID_HANDLE
);
2415 /* If path is already open, do nothing. Check if not Save DC state */
2416 if ((dc
->dclevel
.flPath
& DCPATH_ACTIVE
) && !(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2422 if (dc
->dclevel
.hPath
)
2424 DPRINT("BeginPath 1 0x%p\n", dc
->dclevel
.hPath
);
2425 if (!(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2427 // Remove previous handle.
2428 if (!PATH_Delete(dc
->dclevel
.hPath
))
2436 // Clear flags and Handle.
2437 dc
->dclevel
.flPath
&= ~(DCPATH_SAVE
| DCPATH_ACTIVE
);
2438 dc
->dclevel
.hPath
= NULL
;
2441 pPath
= PATH_CreatePath(NUM_ENTRIES_INITIAL
);
2442 dc
->dclevel
.flPath
|= DCPATH_ACTIVE
; // Set active ASAP!
2443 dc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
2444 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
2445 IntLPtoDP( dc
, &pPath
->pos
, 1 );
2446 DPRINT("BP : Current pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
2447 PATH_UnlockPath(pPath
);
2459 NtGdiCloseFigure(HDC hDC
)
2461 BOOL Ret
= FALSE
; // Default to failure
2465 DPRINT("Enter %s\n", __FUNCTION__
);
2467 pDc
= DC_LockDc(hDC
);
2470 EngSetLastError(ERROR_INVALID_PARAMETER
);
2474 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2481 if (pPath
->state
== PATH_Open
)
2483 IntGdiCloseFigure(pPath
);
2488 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2491 PATH_UnlockPath(pPath
);
2498 NtGdiEndPath(HDC hDC
)
2504 dc
= DC_LockDc(hDC
);
2507 EngSetLastError(ERROR_INVALID_HANDLE
);
2511 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2518 /* Check that path is currently being constructed */
2519 if ((pPath
->state
!= PATH_Open
) || !(dc
->dclevel
.flPath
& DCPATH_ACTIVE
))
2521 DPRINT("EndPath ERROR! 0x%p\n", dc
->dclevel
.hPath
);
2522 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2525 /* Set flag to indicate that path is finished */
2528 DPRINT("EndPath 0x%p\n", dc
->dclevel
.hPath
);
2529 pPath
->state
= PATH_Closed
;
2530 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2533 PATH_UnlockPath(pPath
);
2540 NtGdiFillPath(HDC hDC
)
2543 PPATH pPath
, pNewPath
;
2547 dc
= DC_LockDc(hDC
);
2550 EngSetLastError(ERROR_INVALID_PARAMETER
);
2554 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2561 DC_vPrepareDCsForBlit(dc
, NULL
, NULL
, NULL
);
2563 pdcattr
= dc
->pdcattr
;
2565 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2566 DC_vUpdateLineBrush(dc
);
2568 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2569 DC_vUpdateFillBrush(dc
);
2571 pNewPath
= PATH_FlattenPath(pPath
);
2573 if (pNewPath
->state
!= PATH_Closed
)
2575 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2577 else if (pNewPath
->numEntriesUsed
)
2579 ret
= PATH_FillPath(dc
, pNewPath
);
2583 PATH_UnlockPath(pNewPath
);
2584 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2586 PATH_UnlockPath(pPath
);
2587 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2588 dc
->dclevel
.hPath
= 0;
2589 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2591 DC_vFinishBlit(dc
, NULL
);
2598 NtGdiFlattenPath(HDC hDC
)
2602 PPATH pPath
, pNewPath
= NULL
;
2604 DPRINT("Enter %s\n", __FUNCTION__
);
2606 pDc
= DC_LockDc(hDC
);
2609 EngSetLastError(ERROR_INVALID_HANDLE
);
2613 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2616 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
2621 if (pPath
->state
== PATH_Closed
)
2623 pNewPath
= PATH_FlattenPath(pPath
);
2626 PATH_UnlockPath(pPath
);
2630 PATH_Delete(pDc
->dclevel
.hPath
);
2631 pDc
->dclevel
.hPath
= pNewPath
->BaseObject
.hHmgr
;
2632 PATH_UnlockPath(pNewPath
);
2640 _Success_(return != FALSE
)
2645 _Out_ PDWORD pdwOut
)
2648 BOOL bResult
= TRUE
;
2650 if (!(pDc
= DC_LockDc(hdc
)))
2652 EngSetLastError(ERROR_INVALID_PARAMETER
);
2658 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2659 *pdwOut
= pDc
->dclevel
.laPath
.eMiterLimit
;
2661 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2663 SetLastNtError(_SEH2_GetExceptionCode());
2684 DC
*dc
= DC_LockDc(hDC
);
2685 DPRINT("NtGdiGetPath start\n");
2688 DPRINT1("Can't lock dc!\n");
2689 EngSetLastError(ERROR_INVALID_PARAMETER
);
2693 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2700 if (pPath
->state
!= PATH_Closed
)
2702 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2708 ret
= pPath
->numEntriesUsed
;
2710 else if (nSize
< pPath
->numEntriesUsed
)
2712 EngSetLastError(ERROR_INVALID_PARAMETER
);
2719 memcpy(Points
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
2720 memcpy(Types
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
2722 /* Convert the points to logical coordinates */
2723 if (!GdiPathDPtoLP(dc
, Points
, pPath
->numEntriesUsed
))
2725 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW
);
2729 ret
= pPath
->numEntriesUsed
;
2731 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2733 SetLastNtError(_SEH2_GetExceptionCode());
2739 DPRINT("NtGdiGetPath exit %d\n",ret
);
2740 PATH_UnlockPath(pPath
);
2747 NtGdiPathToRegion(HDC hDC
)
2749 PPATH pPath
, pNewPath
;
2756 DPRINT("Enter %s\n", __FUNCTION__
);
2758 pDc
= DC_LockDc(hDC
);
2761 DPRINT("Failed to lock DC %p\n", hDC
);
2762 EngSetLastError(ERROR_INVALID_PARAMETER
);
2766 pdcattr
= pDc
->pdcattr
;
2768 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2771 DPRINT("Failed to lock DC path %p\n", pDc
->dclevel
.hPath
);
2776 if (pPath
->state
!= PATH_Closed
)
2778 // FIXME: Check that setlasterror is being called correctly
2779 DPRINT("Path is not closed!\n");
2780 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2784 /* Create the region and fill it with the path strokes */
2785 Rgn
= REGION_AllocUserRgnWithHandle(1);
2788 DPRINT("Failed to allocate a region\n");
2789 PATH_UnlockPath(pPath
);
2793 hrgnRval
= Rgn
->BaseObject
.hHmgr
;
2795 pNewPath
= PATH_FlattenPath(pPath
);
2797 Ret
= PATH_PathToRegion(pNewPath
, pdcattr
->jFillMode
, Rgn
);
2799 PATH_UnlockPath(pNewPath
);
2800 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2804 DPRINT("PATH_PathToRegion failed\n");
2809 REGION_UnlockRgn(Rgn
);
2812 PATH_UnlockPath(pPath
);
2813 PATH_Delete(pDc
->dclevel
.hPath
);
2814 pDc
->dclevel
.hPath
= NULL
;
2815 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2826 IN OUT OPTIONAL PDWORD pdwOut
)
2829 gxf_long worker
, worker1
;
2830 BOOL bResult
= TRUE
;
2832 if (!(pDc
= DC_LockDc(hdc
)))
2834 EngSetLastError(ERROR_INVALID_PARAMETER
);
2839 worker1
.f
= pDc
->dclevel
.laPath
.eMiterLimit
;
2840 pDc
->dclevel
.laPath
.eMiterLimit
= worker
.f
;
2846 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2847 *pdwOut
= worker1
.l
;
2849 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2851 SetLastNtError(_SEH2_GetExceptionCode());
2863 NtGdiStrokeAndFillPath(HDC hDC
)
2867 PPATH pPath
, pNewPath
;
2870 DPRINT("Enter %s\n", __FUNCTION__
);
2872 if (!(pDc
= DC_LockDc(hDC
)))
2874 EngSetLastError(ERROR_INVALID_PARAMETER
);
2877 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2884 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2886 pdcattr
= pDc
->pdcattr
;
2888 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2889 DC_vUpdateFillBrush(pDc
);
2891 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2892 DC_vUpdateLineBrush(pDc
);
2894 pNewPath
= PATH_FlattenPath(pPath
);
2896 if (pNewPath
->state
!= PATH_Closed
)
2898 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2900 else if (pNewPath
->numEntriesUsed
)
2902 bRet
= PATH_FillPath(pDc
, pNewPath
);
2903 if (bRet
) bRet
= PATH_StrokePath(pDc
, pNewPath
);
2907 PATH_UnlockPath(pNewPath
);
2908 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2910 PATH_UnlockPath(pPath
);
2911 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2912 pDc
->dclevel
.hPath
= 0;
2913 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2915 DC_vFinishBlit(pDc
, NULL
);
2922 NtGdiStrokePath(HDC hDC
)
2926 PPATH pPath
, pNewPath
;
2929 DPRINT("Enter %s\n", __FUNCTION__
);
2931 if (!(pDc
= DC_LockDc(hDC
)))
2933 EngSetLastError(ERROR_INVALID_PARAMETER
);
2937 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2944 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2946 pdcattr
= pDc
->pdcattr
;
2948 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2949 DC_vUpdateLineBrush(pDc
);
2951 pNewPath
= PATH_FlattenPath(pPath
);
2953 if (pNewPath
->state
!= PATH_Closed
)
2955 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2957 else bRet
= PATH_StrokePath(pDc
, pNewPath
);
2959 PATH_UnlockPath(pNewPath
);
2960 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2962 DC_vFinishBlit(pDc
, NULL
);
2964 PATH_UnlockPath(pPath
);
2965 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2966 pDc
->dclevel
.hPath
= 0;
2967 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2975 NtGdiWidenPath(HDC hDC
)
2979 PDC pdc
= DC_LockDc(hDC
);
2980 DPRINT("NtGdiWidenPat Enter\n");
2983 EngSetLastError(ERROR_INVALID_PARAMETER
);
2987 pPath
= PATH_WidenPath(pdc
);
2990 DPRINT("WindenPath New Path\n");
2991 PATH_Delete(pdc
->dclevel
.hPath
);
2992 pdc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
2996 DPRINT("NtGdiWidenPat Ret %d\n",Ret
);