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 INT mapMode
, graphicsMode
;
1462 SIZE ptViewportExt
, ptWindowExt
;
1463 POINTL ptViewportOrg
, ptWindowOrg
;
1466 PDC_ATTR pdcattr
= dc
->pdcattr
;
1468 /* Allocate a temporary region */
1469 Rgn
= IntSysCreateRectpRgn(0, 0, 0, 0);
1472 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1476 if (!PATH_PathToRegion(pPath
, pdcattr
->jFillMode
, Rgn
))
1478 DPRINT("PFP : Fail P2R\n");
1479 /* EngSetLastError ? */
1484 /* Since PaintRgn interprets the region as being in logical coordinates
1485 * but the points we store for the path are already in device
1486 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
1487 * Using SaveDC to save information about the mapping mode / world
1488 * transform would be easier but would require more overhead, especially
1489 * now that SaveDC saves the current path.
1492 /* Save the information about the old mapping mode */
1493 mapMode
= pdcattr
->iMapMode
;
1494 ptViewportExt
= pdcattr
->szlViewportExt
;
1495 ptViewportOrg
= pdcattr
->ptlViewportOrg
;
1496 ptWindowExt
= pdcattr
->szlWindowExt
;
1497 ptWindowOrg
= pdcattr
->ptlWindowOrg
;
1499 /* Save world transform
1500 * NB: The Windows documentation on world transforms would lead one to
1501 * believe that this has to be done only in GM_ADVANCED; however, my
1502 * tests show that resetting the graphics mode to GM_COMPATIBLE does
1503 * not reset the world transform.
1505 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1508 IntGdiSetMapMode(dc
, MM_TEXT
);
1509 pdcattr
->ptlViewportOrg
.x
= 0;
1510 pdcattr
->ptlViewportOrg
.y
= 0;
1511 pdcattr
->ptlWindowOrg
.x
= 0;
1512 pdcattr
->ptlWindowOrg
.y
= 0;
1514 graphicsMode
= pdcattr
->iGraphicsMode
;
1515 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1516 GreModifyWorldTransform(dc
, &xform
, MWT_IDENTITY
);
1517 pdcattr
->iGraphicsMode
= graphicsMode
;
1519 /* Paint the region */
1520 IntGdiPaintRgn(dc
, Rgn
);
1522 /* Restore the old mapping mode */
1523 IntGdiSetMapMode(dc
, mapMode
);
1524 pdcattr
->szlViewportExt
= ptViewportExt
;
1525 pdcattr
->ptlViewportOrg
= ptViewportOrg
;
1526 pdcattr
->szlWindowExt
= ptWindowExt
;
1527 pdcattr
->ptlWindowOrg
= ptWindowOrg
;
1529 /* Go to GM_ADVANCED temporarily to restore the world transform */
1530 graphicsMode
= pdcattr
->iGraphicsMode
;
1531 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1532 GreModifyWorldTransform(dc
, &xform
, MWT_SET
);
1533 pdcattr
->iGraphicsMode
= graphicsMode
;
1545 INT nLinePts
, nAlloc
;
1546 POINT
*pLinePts
= NULL
;
1547 POINT ptViewportOrg
, ptWindowOrg
;
1548 SIZE szViewportExt
, szWindowExt
;
1549 DWORD mapMode
, graphicsMode
;
1551 PDC_ATTR pdcattr
= dc
->pdcattr
;
1553 DPRINT("Enter %s\n", __FUNCTION__
);
1555 /* Save the mapping mode info */
1556 mapMode
= pdcattr
->iMapMode
;
1558 szViewportExt
= *DC_pszlViewportExt(dc
);
1559 ptViewportOrg
= dc
->pdcattr
->ptlViewportOrg
;
1560 szWindowExt
= dc
->pdcattr
->szlWindowExt
;
1561 ptWindowOrg
= dc
->pdcattr
->ptlWindowOrg
;
1563 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1566 pdcattr
->iMapMode
= MM_TEXT
;
1567 pdcattr
->ptlViewportOrg
.x
= 0;
1568 pdcattr
->ptlViewportOrg
.y
= 0;
1569 pdcattr
->ptlWindowOrg
.x
= 0;
1570 pdcattr
->ptlWindowOrg
.y
= 0;
1571 graphicsMode
= pdcattr
->iGraphicsMode
;
1572 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1573 GreModifyWorldTransform(dc
, (XFORML
*)&xform
, MWT_IDENTITY
);
1574 pdcattr
->iGraphicsMode
= graphicsMode
;
1576 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1577 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1578 * space in case we get one to keep the number of reallocations small. */
1579 nAlloc
= pPath
->numEntriesUsed
+ 1 + 300;
1580 pLinePts
= ExAllocatePoolWithTag(PagedPool
, nAlloc
* sizeof(POINT
), TAG_PATH
);
1583 DPRINT1("Can't allocate pool!\n");
1584 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1589 for (i
= 0; i
< pPath
->numEntriesUsed
; i
++)
1591 if ((i
== 0 || (pPath
->pFlags
[i
- 1] & PT_CLOSEFIGURE
))
1592 && (pPath
->pFlags
[i
] != PT_MOVETO
))
1594 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1595 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1596 (INT
)pPath
->pFlags
[i
]);
1600 switch(pPath
->pFlags
[i
])
1603 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1604 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1605 if (nLinePts
>= 2) IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1607 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1610 case (PT_LINETO
| PT_CLOSEFIGURE
):
1611 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1612 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1613 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1616 DPRINT("Got PT_BEZIERTO\n");
1617 if (pPath
->pFlags
[i
+ 1] != PT_BEZIERTO
||
1618 (pPath
->pFlags
[i
+ 2] & ~PT_CLOSEFIGURE
) != PT_BEZIERTO
)
1620 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1626 INT nBzrPts
, nMinAlloc
;
1627 POINT
*pBzrPts
= GDI_Bezier(&pPath
->pPoints
[i
- 1], 4, &nBzrPts
);
1628 /* Make sure we have allocated enough memory for the lines of
1629 * this bezier and the rest of the path, assuming we won't get
1630 * another one (since we won't reallocate again then). */
1631 nMinAlloc
= nLinePts
+ (pPath
->numEntriesUsed
- i
) + nBzrPts
;
1632 if (nAlloc
< nMinAlloc
)
1634 // Reallocate memory
1636 POINT
*Realloc
= NULL
;
1637 nAlloc
= nMinAlloc
* 2;
1639 Realloc
= ExAllocatePoolWithTag(PagedPool
,
1640 nAlloc
* sizeof(POINT
),
1645 DPRINT1("Can't allocate pool!\n");
1646 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1650 memcpy(Realloc
, pLinePts
, nLinePts
* sizeof(POINT
));
1651 ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1654 memcpy(&pLinePts
[nLinePts
], &pBzrPts
[1], (nBzrPts
- 1) * sizeof(POINT
));
1655 nLinePts
+= nBzrPts
- 1;
1656 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1661 DPRINT1("Got path flag %d (not supported)\n", (INT
)pPath
->pFlags
[i
]);
1665 if (pPath
->pFlags
[i
] & PT_CLOSEFIGURE
)
1667 pLinePts
[nLinePts
++] = pLinePts
[0];
1671 IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1676 if (pLinePts
) ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1678 /* Restore the old mapping mode */
1679 pdcattr
->iMapMode
= mapMode
;
1680 pdcattr
->szlWindowExt
.cx
= szWindowExt
.cx
;
1681 pdcattr
->szlWindowExt
.cy
= szWindowExt
.cy
;
1682 pdcattr
->ptlWindowOrg
.x
= ptWindowOrg
.x
;
1683 pdcattr
->ptlWindowOrg
.y
= ptWindowOrg
.y
;
1685 pdcattr
->szlViewportExt
.cx
= szViewportExt
.cx
;
1686 pdcattr
->szlViewportExt
.cy
= szViewportExt
.cy
;
1687 pdcattr
->ptlViewportOrg
.x
= ptViewportOrg
.x
;
1688 pdcattr
->ptlViewportOrg
.y
= ptViewportOrg
.y
;
1690 /* Restore the world transform */
1691 XForm2MatrixS(&dc
->pdcattr
->mxWorldToPage
, &xform
);
1693 /* If we've moved the current point then get its new position
1694 which will be in device (MM_TEXT) co-ords, convert it to
1695 logical co-ords and re-set it. This basically updates
1696 dc->CurPosX|Y so that their values are in the correct mapping
1702 IntGetCurrentPositionEx(dc
, &pt
);
1703 IntDPtoLP(dc
, &pt
, 1);
1704 IntGdiMoveToEx(dc
, pt
.x
, pt
.y
, NULL
);
1706 DPRINT("Leave %s, ret=%d\n", __FUNCTION__
, ret
);
1710 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1715 PATH_WidenPath(DC
*dc
)
1717 INT i
, j
, numStrokes
, numOldStrokes
, penWidth
, penWidthIn
, penWidthOut
, size
, penStyle
;
1718 PPATH pPath
, flat_path
, pNewPath
, *pStrokes
= NULL
, *pOldStrokes
, pUpPath
, pDownPath
;
1721 DWORD obj_type
, joint
, endcap
, penType
;
1722 PDC_ATTR pdcattr
= dc
->pdcattr
;
1724 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1727 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
1731 if (pPath
->state
!= PATH_Closed
)
1734 PATH_UnlockPath(pPath
);
1735 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1739 size
= GreGetObject(pdcattr
->hpen
, 0, NULL
);
1743 PATH_UnlockPath(pPath
);
1744 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1748 elp
= ExAllocatePoolWithTag(PagedPool
, size
, TAG_PATH
);
1752 PATH_UnlockPath(pPath
);
1753 EngSetLastError(ERROR_OUTOFMEMORY
);
1757 GreGetObject(pdcattr
->hpen
, size
, elp
);
1759 obj_type
= GDI_HANDLE_GET_TYPE(pdcattr
->hpen
);
1760 if (obj_type
== GDI_OBJECT_TYPE_PEN
)
1762 penStyle
= ((LOGPEN
*)elp
)->lopnStyle
;
1764 else if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
)
1766 penStyle
= elp
->elpPenStyle
;
1771 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1772 ExFreePoolWithTag(elp
, TAG_PATH
);
1773 PATH_UnlockPath(pPath
);
1777 penWidth
= elp
->elpWidth
;
1778 ExFreePoolWithTag(elp
, TAG_PATH
);
1780 endcap
= (PS_ENDCAP_MASK
& penStyle
);
1781 joint
= (PS_JOIN_MASK
& penStyle
);
1782 penType
= (PS_TYPE_MASK
& penStyle
);
1784 /* The function cannot apply to cosmetic pens */
1785 if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
&& penType
== PS_COSMETIC
)
1788 PATH_UnlockPath(pPath
);
1789 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
1793 if (!(flat_path
= PATH_FlattenPath(pPath
)))
1795 PATH_UnlockPath(pPath
);
1798 PATH_UnlockPath(pPath
);
1800 penWidthIn
= penWidth
/ 2;
1801 penWidthOut
= penWidth
/ 2;
1802 if (penWidthIn
+ penWidthOut
< penWidth
)
1807 for (i
= 0, j
= 0; i
< flat_path
->numEntriesUsed
; i
++, j
++)
1810 if ((i
== 0 || (flat_path
->pFlags
[i
- 1] & PT_CLOSEFIGURE
)) &&
1811 (flat_path
->pFlags
[i
] != PT_MOVETO
))
1813 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1814 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1815 flat_path
->pFlags
[i
]);
1817 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1818 PATH_UnlockPath(flat_path
);
1819 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1822 switch(flat_path
->pFlags
[i
])
1827 pStrokes
[numStrokes
- 1]->state
= PATH_Closed
;
1829 numOldStrokes
= numStrokes
;
1832 if (numStrokes
== 1)
1833 pStrokes
= ExAllocatePoolWithTag(PagedPool
, sizeof(*pStrokes
), TAG_PATH
);
1836 pOldStrokes
= pStrokes
; // Save old pointer.
1837 pStrokes
= ExAllocatePoolWithTag(PagedPool
, numStrokes
* sizeof(*pStrokes
), TAG_PATH
);
1840 PATH_UnlockPath(flat_path
);
1841 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1844 RtlCopyMemory(pStrokes
, pOldStrokes
, numOldStrokes
* sizeof(PPATH
));
1845 ExFreePoolWithTag(pOldStrokes
, TAG_PATH
); // Free old pointer.
1849 PATH_UnlockPath(flat_path
);
1850 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1853 pStrokes
[numStrokes
- 1] = ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1854 if (!pStrokes
[numStrokes
- 1])
1856 ASSERT(FALSE
); // FIXME
1858 PATH_InitGdiPath(pStrokes
[numStrokes
- 1]);
1859 pStrokes
[numStrokes
- 1]->state
= PATH_Open
;
1861 case (PT_LINETO
| PT_CLOSEFIGURE
):
1862 point
.x
= flat_path
->pPoints
[i
].x
;
1863 point
.y
= flat_path
->pPoints
[i
].y
;
1864 PATH_AddEntry(pStrokes
[numStrokes
- 1], &point
, flat_path
->pFlags
[i
]);
1867 /* Should never happen because of the FlattenPath call */
1868 DPRINT1("Should never happen\n");
1871 DPRINT1("Got path flag %c\n", flat_path
->pFlags
[i
]);
1873 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1874 PATH_UnlockPath(flat_path
);
1875 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1880 pNewPath
= PATH_CreatePath( flat_path
->numEntriesUsed
);
1882 for (i
= 0; i
< numStrokes
; i
++)
1884 pUpPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1885 PATH_InitGdiPath(pUpPath
);
1886 pUpPath
->state
= PATH_Open
;
1887 pDownPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1888 PATH_InitGdiPath(pDownPath
);
1889 pDownPath
->state
= PATH_Open
;
1891 for (j
= 0; j
< pStrokes
[i
]->numEntriesUsed
; j
++)
1893 /* Beginning or end of the path if not closed */
1894 if ((!(pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
)) && (j
== 0 || j
== pStrokes
[i
]->numEntriesUsed
- 1))
1896 /* Compute segment angle */
1897 double xo
, yo
, xa
, ya
, theta
;
1899 FLOAT_POINT corners
[2];
1902 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1903 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1904 xa
= pStrokes
[i
]->pPoints
[1].x
;
1905 ya
= pStrokes
[i
]->pPoints
[1].y
;
1909 xa
= pStrokes
[i
]->pPoints
[j
- 1].x
;
1910 ya
= pStrokes
[i
]->pPoints
[j
- 1].y
;
1911 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1912 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1914 theta
= atan2(ya
- yo
, xa
- xo
);
1917 case PS_ENDCAP_SQUARE
:
1918 pt
.x
= xo
+ round(sqrt(2) * penWidthOut
* cos(M_PI_4
+ theta
));
1919 pt
.y
= yo
+ round(sqrt(2) * penWidthOut
* sin(M_PI_4
+ theta
));
1920 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1921 pt
.x
= xo
+ round(sqrt(2) * penWidthIn
* cos(- M_PI_4
+ theta
));
1922 pt
.y
= yo
+ round(sqrt(2) * penWidthIn
* sin(- M_PI_4
+ theta
));
1923 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1925 case PS_ENDCAP_FLAT
:
1926 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
1927 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
1928 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1929 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
1930 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
1931 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1933 case PS_ENDCAP_ROUND
:
1935 corners
[0].x
= xo
- penWidthIn
;
1936 corners
[0].y
= yo
- penWidthIn
;
1937 corners
[1].x
= xo
+ penWidthOut
;
1938 corners
[1].y
= yo
+ penWidthOut
;
1939 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI_2
, theta
+ 3 * M_PI_4
, (j
== 0 ? PT_MOVETO
: FALSE
));
1940 PATH_DoArcPart(pUpPath
, corners
, theta
+ 3 * M_PI_4
, theta
+ M_PI
, FALSE
);
1941 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI
, theta
+ 5 * M_PI_4
, FALSE
);
1942 PATH_DoArcPart(pUpPath
, corners
, theta
+ 5 * M_PI_4
, theta
+ 3 * M_PI_2
, FALSE
);
1946 /* Corpse of the path */
1951 double xa
, ya
, xb
, yb
, xo
, yo
;
1952 double alpha
, theta
, miterWidth
;
1953 DWORD _joint
= joint
;
1955 PPATH pInsidePath
, pOutsidePath
;
1956 if (j
> 0 && j
< pStrokes
[i
]->numEntriesUsed
- 1)
1963 previous
= pStrokes
[i
]->numEntriesUsed
- 1;
1971 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1972 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1973 xa
= pStrokes
[i
]->pPoints
[previous
].x
;
1974 ya
= pStrokes
[i
]->pPoints
[previous
].y
;
1975 xb
= pStrokes
[i
]->pPoints
[next
].x
;
1976 yb
= pStrokes
[i
]->pPoints
[next
].y
;
1977 theta
= atan2(yo
- ya
, xo
- xa
);
1978 alpha
= atan2(yb
- yo
, xb
- xo
) - theta
;
1979 if (alpha
> 0) alpha
-= M_PI
;
1981 if (_joint
== PS_JOIN_MITER
&& dc
->dclevel
.laPath
.eMiterLimit
< fabs(1 / sin(alpha
/ 2)))
1983 _joint
= PS_JOIN_BEVEL
;
1987 pInsidePath
= pUpPath
;
1988 pOutsidePath
= pDownPath
;
1992 pInsidePath
= pDownPath
;
1993 pOutsidePath
= pUpPath
;
1999 /* Inside angle points */
2002 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
2003 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
2007 pt
.x
= xo
+ round(penWidthIn
* cos(theta
+ M_PI_2
));
2008 pt
.y
= yo
+ round(penWidthIn
* sin(theta
+ M_PI_2
));
2010 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
2013 pt
.x
= xo
+ round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
2014 pt
.y
= yo
+ round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
2018 pt
.x
= xo
- round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
2019 pt
.y
= yo
- round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
2021 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
2022 /* Outside angle point */
2025 case PS_JOIN_MITER
:
2026 miterWidth
= fabs(penWidthOut
/ cos(M_PI_2
- fabs(alpha
) / 2));
2027 pt
.x
= xo
+ round(miterWidth
* cos(theta
+ alpha
/ 2));
2028 pt
.y
= yo
+ round(miterWidth
* sin(theta
+ alpha
/ 2));
2029 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2031 case PS_JOIN_BEVEL
:
2034 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
2035 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
2039 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
2040 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
2042 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2045 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2046 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2050 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2051 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2053 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
2055 case PS_JOIN_ROUND
:
2059 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
2060 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
2064 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
2065 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
2067 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2068 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ alpha
/ 2));
2069 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ alpha
/ 2));
2070 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2073 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2074 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2078 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2079 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2081 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2086 type
= add_points( pNewPath
, pUpPath
->pPoints
, pUpPath
->numEntriesUsed
, PT_LINETO
);
2087 type
[0] = PT_MOVETO
;
2088 reverse_points( pDownPath
->pPoints
, pDownPath
->numEntriesUsed
);
2089 type
= add_points( pNewPath
, pDownPath
->pPoints
, pDownPath
->numEntriesUsed
, PT_LINETO
);
2090 if (pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
) type
[0] = PT_MOVETO
;
2092 PATH_DestroyGdiPath(pStrokes
[i
]);
2093 ExFreePoolWithTag(pStrokes
[i
], TAG_PATH
);
2094 PATH_DestroyGdiPath(pUpPath
);
2095 ExFreePoolWithTag(pUpPath
, TAG_PATH
);
2096 PATH_DestroyGdiPath(pDownPath
);
2097 ExFreePoolWithTag(pDownPath
, TAG_PATH
);
2099 if (pStrokes
) ExFreePoolWithTag(pStrokes
, TAG_PATH
);
2101 PATH_UnlockPath(flat_path
);
2102 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
2103 pNewPath
->state
= PATH_Closed
;
2104 PATH_UnlockPath(pNewPath
);
2108 static inline INT
int_from_fixed(FIXED f
)
2110 return (f
.fract
>= 0x8000) ? (f
.value
+ 1) : f
.value
;
2113 /**********************************************************************
2116 * Internally used by PATH_add_outline
2130 PATH_AddEntry(pPath
, &lppt
[1], PT_LINETO
);
2134 add_points( pPath
, lppt
, 3, PT_BEZIERTO
);
2147 pt
[1] = lppt
[i
+ 1];
2148 pt
[2].x
= (lppt
[i
+ 2].x
+ lppt
[i
+ 1].x
) / 2;
2149 pt
[2].y
= (lppt
[i
+ 2].y
+ lppt
[i
+ 1].y
) / 2;
2150 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2156 pt
[1] = lppt
[i
+ 1];
2157 pt
[2] = lppt
[i
+ 2];
2158 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2170 TTPOLYGONHEADER
*header
,
2173 TTPOLYGONHEADER
*start
;
2175 BOOL bResult
= FALSE
;
2179 while ((char *)header
< (char *)start
+ size
)
2183 if (header
->dwType
!= TT_POLYGON_TYPE
)
2185 DPRINT1("Unknown header type %lu\n", header
->dwType
);
2189 pt
.x
= x
+ int_from_fixed(header
->pfxStart
.x
);
2190 pt
.y
= y
- int_from_fixed(header
->pfxStart
.y
);
2191 PATH_AddEntry(pPath
, &pt
, PT_MOVETO
);
2193 curve
= (TTPOLYCURVE
*)(header
+ 1);
2195 while ((char *)curve
< (char *)header
+ header
->cb
)
2197 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2199 switch(curve
->wType
)
2205 for (i
= 0; i
< curve
->cpfx
; i
++)
2207 pt
.x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2208 pt
.y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2209 PATH_AddEntry(pPath
, &pt
, PT_LINETO
);
2214 case TT_PRIM_QSPLINE
:
2215 case TT_PRIM_CSPLINE
:
2219 POINT
*pts
= ExAllocatePoolWithTag(PagedPool
, (curve
->cpfx
+ 1) * sizeof(POINT
), TAG_PATH
);
2221 if (!pts
) goto cleanup
;
2223 ptfx
= *(POINTFX
*)((char *)curve
- sizeof(POINTFX
));
2225 pts
[0].x
= x
+ int_from_fixed(ptfx
.x
);
2226 pts
[0].y
= y
- int_from_fixed(ptfx
.y
);
2228 for (i
= 0; i
< curve
->cpfx
; i
++)
2230 pts
[i
+ 1].x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2231 pts
[i
+ 1].y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2234 PATH_BezierTo(pPath
, pts
, curve
->cpfx
+ 1);
2236 ExFreePoolWithTag(pts
, TAG_PATH
);
2241 DPRINT1("Unknown curve type %04x\n", curve
->wType
);
2245 curve
= (TTPOLYCURVE
*)&curve
->apfx
[curve
->cpfx
];
2247 header
= (TTPOLYGONHEADER
*)((char *)header
+ header
->cb
);
2253 IntGdiCloseFigure(pPath
);
2257 /**********************************************************************
2273 unsigned int idx
, ggo_flags
= GGO_NATIVE
;
2274 POINT offset
= {0, 0};
2276 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2282 if (pPath
->state
!= PATH_Open
)
2284 DPRINT1("PATH_ExtTextOut not open\n");
2288 if (!count
) return TRUE
;
2289 if (flags
& ETO_GLYPH_INDEX
) ggo_flags
|= GGO_GLYPH_INDEX
;
2291 for (idx
= 0; idx
< count
; idx
++)
2293 MAT2 identity
= { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2298 dwSize
= ftGdiGetGlyphOutline(dc
,
2306 if (dwSize
== GDI_ERROR
)
2308 PATH_UnlockPath(pPath
);
2312 /* Add outline only if char is printable */
2315 outline
= ExAllocatePoolWithTag(PagedPool
, dwSize
, TAG_PATH
);
2318 PATH_UnlockPath(pPath
);
2322 ftGdiGetGlyphOutline(dc
,
2331 PATH_add_outline(dc
, pPath
, x
+ offset
.x
, y
+ offset
.y
, outline
, dwSize
);
2333 ExFreePoolWithTag(outline
, TAG_PATH
);
2338 if (flags
& ETO_PDY
)
2340 offset
.x
+= dx
[idx
* 2];
2341 offset
.y
+= dx
[idx
* 2 + 1];
2344 offset
.x
+= dx
[idx
];
2348 offset
.x
+= gm
.gmCellIncX
;
2349 offset
.y
+= gm
.gmCellIncY
;
2352 PATH_UnlockPath(pPath
);
2357 /***********************************************************************
2358 * Exported functions
2363 NtGdiAbortPath(HDC hDC
)
2365 PDC dc
= DC_LockDc(hDC
);
2368 EngSetLastError(ERROR_INVALID_HANDLE
);
2372 if (!dc
->dclevel
.hPath
)
2378 if (!PATH_Delete(dc
->dclevel
.hPath
))
2384 dc
->dclevel
.hPath
= 0;
2385 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2393 NtGdiBeginPath(HDC hDC
)
2398 dc
= DC_LockDc(hDC
);
2401 EngSetLastError(ERROR_INVALID_HANDLE
);
2405 /* If path is already open, do nothing. Check if not Save DC state */
2406 if ((dc
->dclevel
.flPath
& DCPATH_ACTIVE
) && !(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2412 if (dc
->dclevel
.hPath
)
2414 DPRINT("BeginPath 1 0x%p\n", dc
->dclevel
.hPath
);
2415 if (!(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2417 // Remove previous handle.
2418 if (!PATH_Delete(dc
->dclevel
.hPath
))
2426 // Clear flags and Handle.
2427 dc
->dclevel
.flPath
&= ~(DCPATH_SAVE
| DCPATH_ACTIVE
);
2428 dc
->dclevel
.hPath
= NULL
;
2431 pPath
= PATH_CreatePath(NUM_ENTRIES_INITIAL
);
2432 dc
->dclevel
.flPath
|= DCPATH_ACTIVE
; // Set active ASAP!
2433 dc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
2434 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
2435 IntLPtoDP( dc
, &pPath
->pos
, 1 );
2436 DPRINT("BP : Current pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
2437 PATH_UnlockPath(pPath
);
2449 NtGdiCloseFigure(HDC hDC
)
2451 BOOL Ret
= FALSE
; // Default to failure
2455 DPRINT("Enter %s\n", __FUNCTION__
);
2457 pDc
= DC_LockDc(hDC
);
2460 EngSetLastError(ERROR_INVALID_PARAMETER
);
2464 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2471 if (pPath
->state
== PATH_Open
)
2473 IntGdiCloseFigure(pPath
);
2478 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2481 PATH_UnlockPath(pPath
);
2488 NtGdiEndPath(HDC hDC
)
2494 dc
= DC_LockDc(hDC
);
2497 EngSetLastError(ERROR_INVALID_HANDLE
);
2501 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2508 /* Check that path is currently being constructed */
2509 if ((pPath
->state
!= PATH_Open
) || !(dc
->dclevel
.flPath
& DCPATH_ACTIVE
))
2511 DPRINT("EndPath ERROR! 0x%p\n", dc
->dclevel
.hPath
);
2512 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2515 /* Set flag to indicate that path is finished */
2518 DPRINT("EndPath 0x%p\n", dc
->dclevel
.hPath
);
2519 pPath
->state
= PATH_Closed
;
2520 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2523 PATH_UnlockPath(pPath
);
2530 NtGdiFillPath(HDC hDC
)
2533 PPATH pPath
, pNewPath
;
2537 dc
= DC_LockDc(hDC
);
2540 EngSetLastError(ERROR_INVALID_PARAMETER
);
2544 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2551 DC_vPrepareDCsForBlit(dc
, NULL
, NULL
, NULL
);
2553 pdcattr
= dc
->pdcattr
;
2555 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2556 DC_vUpdateLineBrush(dc
);
2558 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2559 DC_vUpdateFillBrush(dc
);
2561 pNewPath
= PATH_FlattenPath(pPath
);
2563 if (pNewPath
->state
!= PATH_Closed
)
2565 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2567 else if (pNewPath
->numEntriesUsed
)
2569 ret
= PATH_FillPath(dc
, pNewPath
);
2573 PATH_UnlockPath(pNewPath
);
2574 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2576 PATH_UnlockPath(pPath
);
2577 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2578 dc
->dclevel
.hPath
= 0;
2579 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2581 DC_vFinishBlit(dc
, NULL
);
2588 NtGdiFlattenPath(HDC hDC
)
2592 PPATH pPath
, pNewPath
= NULL
;
2594 DPRINT("Enter %s\n", __FUNCTION__
);
2596 pDc
= DC_LockDc(hDC
);
2599 EngSetLastError(ERROR_INVALID_HANDLE
);
2603 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2606 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
2611 if (pPath
->state
== PATH_Closed
)
2613 pNewPath
= PATH_FlattenPath(pPath
);
2616 PATH_UnlockPath(pPath
);
2620 PATH_Delete(pDc
->dclevel
.hPath
);
2621 pDc
->dclevel
.hPath
= pNewPath
->BaseObject
.hHmgr
;
2622 PATH_UnlockPath(pNewPath
);
2630 _Success_(return != FALSE
)
2635 _Out_ PDWORD pdwOut
)
2638 BOOL bResult
= TRUE
;
2640 if (!(pDc
= DC_LockDc(hdc
)))
2642 EngSetLastError(ERROR_INVALID_PARAMETER
);
2648 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2649 *pdwOut
= pDc
->dclevel
.laPath
.eMiterLimit
;
2651 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2653 SetLastNtError(_SEH2_GetExceptionCode());
2674 DC
*dc
= DC_LockDc(hDC
);
2675 DPRINT("NtGdiGetPath start\n");
2678 DPRINT1("Can't lock dc!\n");
2679 EngSetLastError(ERROR_INVALID_PARAMETER
);
2683 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2690 if (pPath
->state
!= PATH_Closed
)
2692 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2698 ret
= pPath
->numEntriesUsed
;
2700 else if (nSize
< pPath
->numEntriesUsed
)
2702 EngSetLastError(ERROR_INVALID_PARAMETER
);
2709 memcpy(Points
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
2710 memcpy(Types
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
2712 /* Convert the points to logical coordinates */
2713 if (!GdiPathDPtoLP(dc
, Points
, pPath
->numEntriesUsed
))
2715 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW
);
2719 ret
= pPath
->numEntriesUsed
;
2721 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2723 SetLastNtError(_SEH2_GetExceptionCode());
2729 DPRINT("NtGdiGetPath exit %d\n",ret
);
2730 PATH_UnlockPath(pPath
);
2737 NtGdiPathToRegion(HDC hDC
)
2739 PPATH pPath
, pNewPath
;
2746 DPRINT("Enter %s\n", __FUNCTION__
);
2748 pDc
= DC_LockDc(hDC
);
2751 DPRINT("Failed to lock DC %p\n", hDC
);
2752 EngSetLastError(ERROR_INVALID_PARAMETER
);
2756 pdcattr
= pDc
->pdcattr
;
2758 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2761 DPRINT("Failed to lock DC path %p\n", pDc
->dclevel
.hPath
);
2766 if (pPath
->state
!= PATH_Closed
)
2768 // FIXME: Check that setlasterror is being called correctly
2769 DPRINT("Path is not closed!\n");
2770 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2774 /* Create the region and fill it with the path strokes */
2775 Rgn
= REGION_AllocUserRgnWithHandle(1);
2778 DPRINT("Failed to allocate a region\n");
2779 PATH_UnlockPath(pPath
);
2783 hrgnRval
= Rgn
->BaseObject
.hHmgr
;
2785 pNewPath
= PATH_FlattenPath(pPath
);
2787 Ret
= PATH_PathToRegion(pNewPath
, pdcattr
->jFillMode
, Rgn
);
2789 PATH_UnlockPath(pNewPath
);
2790 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2794 DPRINT("PATH_PathToRegion failed\n");
2799 REGION_UnlockRgn(Rgn
);
2802 PATH_UnlockPath(pPath
);
2803 PATH_Delete(pDc
->dclevel
.hPath
);
2804 pDc
->dclevel
.hPath
= NULL
;
2805 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2816 IN OUT OPTIONAL PDWORD pdwOut
)
2819 gxf_long worker
, worker1
;
2820 BOOL bResult
= TRUE
;
2822 if (!(pDc
= DC_LockDc(hdc
)))
2824 EngSetLastError(ERROR_INVALID_PARAMETER
);
2829 worker1
.f
= pDc
->dclevel
.laPath
.eMiterLimit
;
2830 pDc
->dclevel
.laPath
.eMiterLimit
= worker
.f
;
2836 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2837 *pdwOut
= worker1
.l
;
2839 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2841 SetLastNtError(_SEH2_GetExceptionCode());
2853 NtGdiStrokeAndFillPath(HDC hDC
)
2857 PPATH pPath
, pNewPath
;
2860 DPRINT("Enter %s\n", __FUNCTION__
);
2862 if (!(pDc
= DC_LockDc(hDC
)))
2864 EngSetLastError(ERROR_INVALID_PARAMETER
);
2867 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2874 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2876 pdcattr
= pDc
->pdcattr
;
2878 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2879 DC_vUpdateFillBrush(pDc
);
2881 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2882 DC_vUpdateLineBrush(pDc
);
2884 pNewPath
= PATH_FlattenPath(pPath
);
2886 if (pNewPath
->state
!= PATH_Closed
)
2888 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2890 else if (pNewPath
->numEntriesUsed
)
2892 bRet
= PATH_FillPath(pDc
, pNewPath
);
2893 if (bRet
) bRet
= PATH_StrokePath(pDc
, pNewPath
);
2897 PATH_UnlockPath(pNewPath
);
2898 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2900 PATH_UnlockPath(pPath
);
2901 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2902 pDc
->dclevel
.hPath
= 0;
2903 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2905 DC_vFinishBlit(pDc
, NULL
);
2912 NtGdiStrokePath(HDC hDC
)
2916 PPATH pPath
, pNewPath
;
2919 DPRINT("Enter %s\n", __FUNCTION__
);
2921 if (!(pDc
= DC_LockDc(hDC
)))
2923 EngSetLastError(ERROR_INVALID_PARAMETER
);
2927 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2934 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2936 pdcattr
= pDc
->pdcattr
;
2938 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2939 DC_vUpdateLineBrush(pDc
);
2941 pNewPath
= PATH_FlattenPath(pPath
);
2943 if (pNewPath
->state
!= PATH_Closed
)
2945 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2947 else bRet
= PATH_StrokePath(pDc
, pNewPath
);
2949 PATH_UnlockPath(pNewPath
);
2950 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2952 DC_vFinishBlit(pDc
, NULL
);
2954 PATH_UnlockPath(pPath
);
2955 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2956 pDc
->dclevel
.hPath
= 0;
2957 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2965 NtGdiWidenPath(HDC hDC
)
2969 PDC pdc
= DC_LockDc(hDC
);
2970 DPRINT("NtGdiWidenPat Enter\n");
2973 EngSetLastError(ERROR_INVALID_PARAMETER
);
2977 pPath
= PATH_WidenPath(pdc
);
2980 DPRINT("WindenPath New Path\n");
2981 PATH_Delete(pdc
->dclevel
.hPath
);
2982 pdc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
2986 DPRINT("NtGdiWidenPat Ret %d\n",Ret
);