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
9 * 2018 Katayama Hirofumi MZ
19 #pragma warning(disable:4244)
22 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
24 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
25 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
28 static int PathCount
= 0;
31 /***********************************************************************
36 PATH_CreatePath(int count
)
38 PPATH pPath
= PATH_AllocPathWithHandle();
42 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
46 DPRINT("CreatePath p 0x%p\n", pPath
);
47 // Path handles are shared. Also due to recursion with in the same thread.
48 GDIOBJ_vUnlockObject((POBJ
)pPath
); // Unlock
49 pPath
= PATH_LockPath(pPath
->BaseObject
.hHmgr
); // Share Lock.
51 /* Make sure that path is empty */
52 PATH_EmptyPath(pPath
);
54 count
= max( NUM_ENTRIES_INITIAL
, count
);
56 pPath
->numEntriesAllocated
= count
;
58 pPath
->pPoints
= (POINT
*)ExAllocatePoolWithTag(PagedPool
, count
* sizeof(POINT
), TAG_PATH
);
59 RtlZeroMemory( pPath
->pPoints
, count
* sizeof(POINT
));
60 pPath
->pFlags
= (BYTE
*)ExAllocatePoolWithTag(PagedPool
, count
* sizeof(BYTE
), TAG_PATH
);
61 RtlZeroMemory( pPath
->pFlags
, count
* sizeof(BYTE
));
63 /* Initialize variables for new path */
64 pPath
->numEntriesUsed
= 0;
65 pPath
->newStroke
= TRUE
;
66 pPath
->state
= PATH_Open
;
67 pPath
->pos
.x
= pPath
->pos
.y
= 0;
70 DPRINT("Create Path %d\n",PathCount
);
75 /* PATH_DestroyGdiPath
77 * Destroys a GdiPath structure (frees the memory in the arrays).
81 PATH_DestroyGdiPath(PPATH pPath
)
83 ASSERT(pPath
!= NULL
);
85 if (pPath
->pPoints
) ExFreePoolWithTag(pPath
->pPoints
, TAG_PATH
);
86 if (pPath
->pFlags
) ExFreePoolWithTag(pPath
->pFlags
, TAG_PATH
);
91 PATH_Delete(HPATH hPath
)
94 if (!hPath
) return FALSE
;
95 pPath
= PATH_LockPath(hPath
);
96 if (!pPath
) return FALSE
;
97 PATH_DestroyGdiPath(pPath
);
98 GDIOBJ_vDeleteObject(&pPath
->BaseObject
);
101 DPRINT("Delete Path %d\n",PathCount
);
109 IntGdiCloseFigure(PPATH pPath
)
111 ASSERT(pPath
->state
== PATH_Open
);
113 // FIXME: Shouldn't we draw a line to the beginning of the figure?
114 // Set PT_CLOSEFIGURE on the last entry and start a new stroke
115 if (pPath
->numEntriesUsed
)
117 pPath
->pFlags
[pPath
->numEntriesUsed
- 1] |= PT_CLOSEFIGURE
;
118 pPath
->newStroke
= TRUE
;
122 /* MSDN: This fails if the device coordinates exceed 27 bits, or if the converted
123 logical coordinates exceed 32 bits. */
133 XFORMOBJ_vInit(&xo
, &pdc
->pdcattr
->mxDeviceToWorld
);
134 return XFORMOBJ_bApplyXform(&xo
, XF_LTOL
, count
, (PPOINTL
)ppt
, (PPOINTL
)ppt
);
139 * Initializes the GdiPath structure.
146 ASSERT(pPath
!= NULL
);
148 pPath
->state
= PATH_Null
;
149 pPath
->pPoints
= NULL
;
150 pPath
->pFlags
= NULL
;
151 pPath
->numEntriesUsed
= 0;
152 pPath
->numEntriesAllocated
= 0;
155 /* PATH_AssignGdiPath
157 * Copies the GdiPath structure "pPathSrc" to "pPathDest". A deep copy is
158 * performed, i.e. the contents of the pPoints and pFlags arrays are copied,
159 * not just the pointers. Since this means that the arrays in pPathDest may
160 * need to be resized, pPathDest should have been initialized using
161 * PATH_InitGdiPath (in C++, this function would be an assignment operator,
162 * not a copy constructor).
163 * Returns TRUE if successful, else FALSE.
169 const PPATH pPathSrc
)
171 ASSERT(pPathDest
!= NULL
&& pPathSrc
!= NULL
);
173 /* Make sure destination arrays are big enough */
174 if (!PATH_ReserveEntries(pPathDest
, pPathSrc
->numEntriesUsed
))
177 /* Perform the copy operation */
178 memcpy(pPathDest
->pPoints
, pPathSrc
->pPoints
, sizeof(POINT
)*pPathSrc
->numEntriesUsed
);
179 memcpy(pPathDest
->pFlags
, pPathSrc
->pFlags
, sizeof(BYTE
)*pPathSrc
->numEntriesUsed
);
181 pPathDest
->pos
= pPathSrc
->pos
;
182 pPathDest
->state
= pPathSrc
->state
;
183 pPathDest
->numEntriesUsed
= pPathSrc
->numEntriesUsed
;
184 pPathDest
->newStroke
= pPathSrc
->newStroke
;
188 BOOL
PATH_SavePath( DC
*dst
, DC
*src
)
190 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
191 DPRINT("PATH_SavePath\n");
194 DPRINT("PATH_SavePath 1\n");
196 pdstPath
= PATH_CreatePath(psrcPath
->numEntriesAllocated
);
198 dst
->dclevel
.flPath
= src
->dclevel
.flPath
;
200 dst
->dclevel
.hPath
= pdstPath
->BaseObject
.hHmgr
;
202 PATH_AssignGdiPath(pdstPath
, psrcPath
);
204 PATH_UnlockPath(pdstPath
);
205 PATH_UnlockPath(psrcPath
);
210 BOOL
PATH_RestorePath( DC
*dst
, DC
*src
)
212 DPRINT("PATH_RestorePath\n");
214 if (dst
->dclevel
.hPath
== NULL
)
216 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
217 DPRINT("PATH_RestorePath 1\n");
218 pdstPath
= PATH_CreatePath(psrcPath
->numEntriesAllocated
);
219 dst
->dclevel
.flPath
= src
->dclevel
.flPath
;
220 dst
->dclevel
.hPath
= pdstPath
->BaseObject
.hHmgr
;
222 PATH_AssignGdiPath(pdstPath
, psrcPath
);
224 PATH_UnlockPath(pdstPath
);
225 PATH_UnlockPath(psrcPath
);
229 PPATH pdstPath
, psrcPath
= PATH_LockPath(src
->dclevel
.hPath
);
230 pdstPath
= PATH_LockPath(dst
->dclevel
.hPath
);
231 DPRINT("PATH_RestorePath 2\n");
232 dst
->dclevel
.flPath
= src
->dclevel
.flPath
& (DCPATH_CLOCKWISE
|DCPATH_ACTIVE
);
233 PATH_AssignGdiPath(pdstPath
, psrcPath
);
235 PATH_UnlockPath(pdstPath
);
236 PATH_UnlockPath(psrcPath
);
243 * Removes all entries from the path and sets the path state to PATH_Null.
247 PATH_EmptyPath(PPATH pPath
)
249 ASSERT(pPath
!= NULL
);
251 pPath
->state
= PATH_Null
;
252 pPath
->numEntriesUsed
= 0;
257 * Adds an entry to the path. For "flags", pass either PT_MOVETO, PT_LINETO
258 * or PT_BEZIERTO, optionally ORed with PT_CLOSEFIGURE. Returns TRUE if
259 * successful, FALSE otherwise (e.g. if not enough memory was available).
268 ASSERT(pPath
!= NULL
);
270 /* FIXME: If newStroke is true, perhaps we want to check that we're
271 * getting a PT_MOVETO
273 DPRINT("(%d,%d) - %d\n", pPoint
->x
, pPoint
->y
, flags
);
275 /* Reserve enough memory for an extra path entry */
276 if (!PATH_ReserveEntries(pPath
, pPath
->numEntriesUsed
+ 1))
279 /* Store information in path entry */
280 pPath
->pPoints
[pPath
->numEntriesUsed
] = *pPoint
;
281 pPath
->pFlags
[pPath
->numEntriesUsed
] = flags
;
283 /* Increment entry count */
284 pPath
->numEntriesUsed
++;
289 /* PATH_ReserveEntries
291 * Ensures that at least "numEntries" entries (for points and flags) have
292 * been allocated; allocates larger arrays and copies the existing entries
293 * to those arrays, if necessary. Returns TRUE if successful, else FALSE.
301 INT numEntriesToAllocate
;
305 ASSERT(pPath
!= NULL
);
306 ASSERT(numEntries
>= 0);
308 /* Do we have to allocate more memory? */
309 if (numEntries
> pPath
->numEntriesAllocated
)
311 /* Find number of entries to allocate. We let the size of the array
312 * grow exponentially, since that will guarantee linear time
314 if (pPath
->numEntriesAllocated
)
316 numEntriesToAllocate
= pPath
->numEntriesAllocated
;
317 while (numEntriesToAllocate
< numEntries
)
318 numEntriesToAllocate
= numEntriesToAllocate
* GROW_FACTOR_NUMER
/ GROW_FACTOR_DENOM
;
321 numEntriesToAllocate
= numEntries
;
323 /* Allocate new arrays */
324 pPointsNew
= (POINT
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(POINT
), TAG_PATH
);
328 pFlagsNew
= (BYTE
*)ExAllocatePoolWithTag(PagedPool
, numEntriesToAllocate
* sizeof(BYTE
), TAG_PATH
);
331 ExFreePoolWithTag(pPointsNew
, TAG_PATH
);
335 /* Copy old arrays to new arrays and discard old arrays */
338 ASSERT(pPath
->pFlags
);
340 memcpy(pPointsNew
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
341 memcpy(pFlagsNew
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
343 ExFreePoolWithTag(pPath
->pPoints
, TAG_PATH
);
344 ExFreePoolWithTag(pPath
->pFlags
, TAG_PATH
);
347 pPath
->pPoints
= pPointsNew
;
348 pPath
->pFlags
= pFlagsNew
;
349 pPath
->numEntriesAllocated
= numEntriesToAllocate
;
355 /* PATH_ScaleNormalizedPoint
357 * Scales a normalized point (x, y) with respect to the box whose corners are
358 * passed in "corners". The point is stored in "*pPoint". The normalized
359 * coordinates (-1.0, -1.0) correspond to corners[0], the coordinates
360 * (1.0, 1.0) correspond to corners[1].
364 PATH_ScaleNormalizedPoint(
365 FLOAT_POINT corners
[],
373 pPoint
->x
= GDI_ROUND((double)corners
[0].x
+ (double)(corners
[1].x
- corners
[0].x
) * 0.5 * (x
+ 1.0));
374 pPoint
->y
= GDI_ROUND((double)corners
[0].y
+ (double)(corners
[1].y
- corners
[0].y
) * 0.5 * (y
+ 1.0));
377 /* PATH_NormalizePoint
379 * Normalizes a point with respect to the box whose corners are passed in
380 * corners. The normalized coordinates are stored in *pX and *pY.
385 FLOAT_POINT corners
[],
386 const FLOAT_POINT
*pPoint
,
395 *pX
= (double)(pPoint
->x
- corners
[0].x
) / (double)(corners
[1].x
- corners
[0].x
) * 2.0 - 1.0;
396 *pY
= (double)(pPoint
->y
- corners
[0].y
) / (double)(corners
[1].y
- corners
[0].y
) * 2.0 - 1.0;
401 * Helper function for PATH_RoundRect() and PATH_Rectangle()
413 PDC_ATTR pdcattr
= dc
->pdcattr
;
415 /* Convert points to device coordinates */
420 IntLPtoDP(dc
, corners
, 2);
422 /* Make sure first corner is top left and second corner is bottom right */
423 if (corners
[0].x
> corners
[1].x
)
426 corners
[0].x
= corners
[1].x
;
430 if (corners
[0].y
> corners
[1].y
)
433 corners
[0].y
= corners
[1].y
;
437 /* In GM_COMPATIBLE, don't include bottom and right edges */
438 if (pdcattr
->iGraphicsMode
== GM_COMPATIBLE
)
440 if (corners
[0].x
== corners
[1].x
) return FALSE
;
441 if (corners
[0].y
== corners
[1].y
) return FALSE
;
448 /* add a number of points, converting them to device coords */
449 /* return a pointer to the first type byte so it can be fixed up if necessary */
450 static BYTE
*add_log_points( DC
*dc
, PPATH path
, const POINT
*points
,
451 DWORD count
, BYTE type
)
455 if (!PATH_ReserveEntries( path
, path
->numEntriesUsed
+ count
)) return NULL
;
457 ret
= &path
->pFlags
[path
->numEntriesUsed
];
458 memcpy( &path
->pPoints
[path
->numEntriesUsed
], points
, count
* sizeof(*points
) );
459 IntLPtoDP( dc
, &path
->pPoints
[path
->numEntriesUsed
], count
);
460 memset( ret
, type
, count
);
461 path
->numEntriesUsed
+= count
;
465 /* add a number of points that are already in device coords */
466 /* return a pointer to the first type byte so it can be fixed up if necessary */
467 static BYTE
*add_points( PPATH path
, const POINT
*points
, DWORD count
, BYTE type
)
471 if (!PATH_ReserveEntries( path
, path
->numEntriesUsed
+ count
)) return NULL
;
473 ret
= &path
->pFlags
[path
->numEntriesUsed
];
474 memcpy( &path
->pPoints
[path
->numEntriesUsed
], points
, count
* sizeof(*points
) );
475 memset( ret
, type
, count
);
476 path
->numEntriesUsed
+= count
;
480 /* reverse the order of an array of points */
481 static void reverse_points( POINT
*points
, UINT count
)
484 for (i
= 0; i
< count
/ 2; i
++)
486 POINT pt
= points
[i
];
487 points
[i
] = points
[count
- i
- 1];
488 points
[count
- i
- 1] = pt
;
492 /* start a new path stroke if necessary */
493 static BOOL
start_new_stroke( PPATH path
)
495 if (!path
->newStroke
&& path
->numEntriesUsed
&&
496 !(path
->pFlags
[path
->numEntriesUsed
- 1] & PT_CLOSEFIGURE
) &&
497 path
->pPoints
[path
->numEntriesUsed
- 1].x
== path
->pos
.x
&&
498 path
->pPoints
[path
->numEntriesUsed
- 1].y
== path
->pos
.y
)
501 path
->newStroke
= FALSE
;
502 return add_points( path
, &path
->pos
, 1, PT_MOVETO
) != NULL
;
505 /* set current position to the last point that was added to the path */
506 static void update_current_pos( PPATH path
)
508 assert( path
->numEntriesUsed
);
509 path
->pos
= path
->pPoints
[path
->numEntriesUsed
- 1];
512 /* close the current figure */
513 static void close_figure( PPATH path
)
515 assert( path
->numEntriesUsed
);
516 path
->pFlags
[path
->numEntriesUsed
- 1] |= PT_CLOSEFIGURE
;
519 /* add a number of points, starting a new stroke if necessary */
520 static BOOL
add_log_points_new_stroke( DC
*dc
, PPATH path
, const POINT
*points
,
521 DWORD count
, BYTE type
)
523 if (!start_new_stroke( path
)) return FALSE
;
524 if (!add_log_points( dc
, path
, points
, count
, type
)) return FALSE
;
525 update_current_pos( path
);
527 DPRINT("ALPNS : Pos X %d Y %d\n",path
->pos
.x
, path
->pos
.y
);
528 IntGdiMoveToEx(dc
, path
->pos
.x
, path
->pos
.y
, NULL
);
535 * Should be called when a MoveTo is performed on a DC that has an
536 * open path. This starts a new stroke. Returns TRUE if successful, else
545 if (!pPath
) return FALSE
;
547 // GDI32 : Signal from user space of a change in position.
548 if (dc
->pdcattr
->ulDirty_
& DIRTY_STYLESTATE
)
550 DPRINT("MoveTo has changed\n");
551 pPath
->newStroke
= TRUE
;
552 // Set position and clear the signal flag.
553 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
554 IntLPtoDP( dc
, &pPath
->pos
, 1 );
563 * Should be called when a LineTo is performed on a DC that has an
564 * open path. This adds a PT_LINETO entry to the path (and possibly
565 * a PT_MOVETO entry, if this is the first LineTo in a stroke).
566 * Returns TRUE if successful, else FALSE.
577 POINT point
, pointCurPos
;
579 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
580 if (!pPath
) return FALSE
;
585 // Coalesce a MoveTo point.
586 if ( !PATH_MoveTo(dc
, pPath
) )
588 /* Add a PT_MOVETO if necessary */
589 if (pPath
->newStroke
)
591 DPRINT("Line To : New Stroke\n");
592 pPath
->newStroke
= FALSE
;
593 IntGetCurrentPositionEx(dc
, &pointCurPos
);
594 CoordLPtoDP(dc
, &pointCurPos
);
595 if (!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
597 PATH_UnlockPath(pPath
);
602 Ret
= add_log_points_new_stroke( dc
, pPath
, &point
, 1, PT_LINETO
);
603 PATH_UnlockPath(pPath
);
609 * Should be called when a call to Rectangle is performed on a DC that has
610 * an open path. Returns TRUE if successful, else FALSE.
622 POINT corners
[2], points
[4];
625 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
626 if (!pPath
) return FALSE
;
628 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
630 PATH_UnlockPath(pPath
);
634 points
[0].x
= corners
[1].x
;
635 points
[0].y
= corners
[0].y
;
636 points
[1] = corners
[0];
637 points
[2].x
= corners
[0].x
;
638 points
[2].y
= corners
[1].y
;
639 points
[3] = corners
[1];
641 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 4 );
643 if (!(type
= add_points( pPath
, points
, 4, PT_LINETO
)))
645 PATH_UnlockPath(pPath
);
650 /* Close the rectangle figure */
651 IntGdiCloseFigure(pPath
) ;
652 PATH_UnlockPath(pPath
);
658 * Should be called when a call to RoundRect is performed on a DC that has
659 * an open path. Returns TRUE if successful, else FALSE.
673 const double factor
= 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
675 POINT corners
[2], ellipse
[2], points
[16];
677 double width
, height
;
679 if (!ell_width
|| !ell_height
) return PATH_Rectangle( dc
, x1
, y1
, x2
, y2
);
681 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
682 if (!pPath
) return FALSE
;
684 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
686 PATH_UnlockPath(pPath
);
690 ellipse
[0].x
= ellipse
[0].y
= 0;
691 ellipse
[1].x
= ell_width
;
692 ellipse
[1].y
= ell_height
;
693 IntLPtoDP( dc
, &ellipse
, 2 );
694 ell_width
= min( abs( ellipse
[1].x
- ellipse
[0].x
), corners
[1].x
- corners
[0].x
);
695 ell_height
= min( abs( ellipse
[1].y
- ellipse
[0].y
), corners
[1].y
- corners
[0].y
);
696 width
= ell_width
/ 2.0;
697 height
= ell_height
/ 2.0;
700 points
[0].x
= corners
[1].x
;
701 points
[0].y
= corners
[0].y
+ GDI_ROUND( height
);
703 points
[1].x
= corners
[1].x
;
704 points
[1].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
705 points
[2].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
706 points
[2].y
= corners
[0].y
;
707 points
[3].x
= corners
[1].x
- GDI_ROUND( width
);
708 points
[3].y
= corners
[0].y
;
709 /* horizontal line */
710 points
[4].x
= corners
[0].x
+ GDI_ROUND( width
);
711 points
[4].y
= corners
[0].y
;
713 points
[5].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
714 points
[5].y
= corners
[0].y
;
715 points
[6].x
= corners
[0].x
;
716 points
[6].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
717 points
[7].x
= corners
[0].x
;
718 points
[7].y
= corners
[0].y
+ GDI_ROUND( height
);
720 points
[8].x
= corners
[0].x
;
721 points
[8].y
= corners
[1].y
- GDI_ROUND( height
);
723 points
[9].x
= corners
[0].x
;
724 points
[9].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
725 points
[10].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
726 points
[10].y
= corners
[1].y
;
727 points
[11].x
= corners
[0].x
+ GDI_ROUND( width
);
728 points
[11].y
= corners
[1].y
;
729 /* horizontal line */
730 points
[12].x
= corners
[1].x
- GDI_ROUND( width
);
731 points
[12].y
= corners
[1].y
;
733 points
[13].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
734 points
[13].y
= corners
[1].y
;
735 points
[14].x
= corners
[1].x
;
736 points
[14].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
737 points
[15].x
= corners
[1].x
;
738 points
[15].y
= corners
[1].y
- GDI_ROUND( height
);
740 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 16 );
741 if (!(type
= add_points( pPath
, points
, 16, PT_BEZIERTO
)))
743 PATH_UnlockPath(pPath
);
747 type
[4] = type
[8] = type
[12] = PT_LINETO
;
749 IntGdiCloseFigure(pPath
);
750 PATH_UnlockPath(pPath
);
766 const double factor
= 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
768 POINT corners
[2], points
[13];
770 double width
, height
;
772 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
773 if (!pPath
) return FALSE
;
775 if (!PATH_CheckCorners(dc
, corners
, x1
, y1
, x2
, y2
))
777 PATH_UnlockPath(pPath
);
781 width
= (corners
[1].x
- corners
[0].x
) / 2.0;
782 height
= (corners
[1].y
- corners
[0].y
) / 2.0;
785 points
[0].x
= corners
[1].x
;
786 points
[0].y
= corners
[0].y
+ GDI_ROUND( height
);
788 points
[1].x
= corners
[1].x
;
789 points
[1].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
790 points
[2].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
791 points
[2].y
= corners
[0].y
;
792 points
[3].x
= corners
[0].x
+ GDI_ROUND( width
);
793 points
[3].y
= corners
[0].y
;
795 points
[4].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
796 points
[4].y
= corners
[0].y
;
797 points
[5].x
= corners
[0].x
;
798 points
[5].y
= corners
[0].y
+ GDI_ROUND( height
* (1 - factor
) );
799 points
[6].x
= corners
[0].x
;
800 points
[6].y
= corners
[0].y
+ GDI_ROUND( height
);
802 points
[7].x
= corners
[0].x
;
803 points
[7].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
804 points
[8].x
= corners
[0].x
+ GDI_ROUND( width
* (1 - factor
) );
805 points
[8].y
= corners
[1].y
;
806 points
[9].x
= corners
[0].x
+ GDI_ROUND( width
);
807 points
[9].y
= corners
[1].y
;
809 points
[10].x
= corners
[1].x
- GDI_ROUND( width
* (1 - factor
) );
810 points
[10].y
= corners
[1].y
;
811 points
[11].x
= corners
[1].x
;
812 points
[11].y
= corners
[1].y
- GDI_ROUND( height
* (1 - factor
) );
813 points
[12].x
= corners
[1].x
;
814 points
[12].y
= corners
[1].y
- GDI_ROUND( height
);
816 if (dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) reverse_points( points
, 13 );
817 if (!(type
= add_points( pPath
, points
, 13, PT_BEZIERTO
)))
819 DPRINT1("PATH_Ellipse No add\n");
820 PATH_UnlockPath(pPath
);
825 IntGdiCloseFigure(pPath
);
826 PATH_UnlockPath(pPath
);
832 * Creates a Bezier spline that corresponds to part of an arc and appends the
833 * corresponding points to the path. The start and end angles are passed in
834 * "angleStart" and "angleEnd"; these angles should span a quarter circle
835 * at most. If "startEntryType" is non-zero, an entry of that type for the first
836 * control point is added to the path; otherwise, it is assumed that the current
837 * position is equal to the first control point.
843 FLOAT_POINT corners
[],
849 double xNorm
[4], yNorm
[4];
854 ASSERT(fabs(angleEnd
- angleStart
) <= M_PI_2
);
856 /* FIXME: Is there an easier way of computing this? */
858 /* Compute control points */
859 halfAngle
= (angleEnd
- angleStart
) / 2.0;
860 if (fabs(halfAngle
) > 1e-8)
862 a
= 4.0 / 3.0 * (1 - cos(halfAngle
)) / sin(halfAngle
);
863 xNorm
[0] = cos(angleStart
);
864 yNorm
[0] = sin(angleStart
);
865 xNorm
[1] = xNorm
[0] - a
* yNorm
[0];
866 yNorm
[1] = yNorm
[0] + a
* xNorm
[0];
867 xNorm
[3] = cos(angleEnd
);
868 yNorm
[3] = sin(angleEnd
);
869 xNorm
[2] = xNorm
[3] + a
* yNorm
[3];
870 yNorm
[2] = yNorm
[3] - a
* xNorm
[3];
873 for (i
= 0; i
< 4; i
++)
875 xNorm
[i
] = cos(angleStart
);
876 yNorm
[i
] = sin(angleStart
);
879 /* Add starting point to path if desired */
880 start
= !startEntryType
;
882 /* Add remaining control points */
883 for (i
= start
; i
< 4; i
++) PATH_ScaleNormalizedPoint(corners
, xNorm
[i
], yNorm
[i
], &points
[i
]);
884 if (!(type
= add_points( pPath
, points
+ start
, 4 - start
, PT_BEZIERTO
))) return FALSE
;
885 if (!start
) type
[0] = startEntryType
;
892 * Should be called when a call to Arc is performed on a DC that has
893 * an open path. This adds up to five Bezier splines representing the arc
894 * to the path. When 'lines' is 1, we add 1 extra line to get a chord,
895 * when 'lines' is 2, we add 2 extra lines to get a pie, and when 'lines' is
896 * -1 we add 1 extra line from the current DC position to the starting position
897 * of the arc before drawing the arc itself (arcto). Returns TRUE if successful,
915 double angleStart
, angleEnd
, angleStartQuadrant
, angleEndQuadrant
= 0.0;
916 /* Initialize angleEndQuadrant to silence gcc's warning */
918 FLOAT_POINT corners
[2], pointStart
, pointEnd
;
919 POINT centre
, pointCurPos
;
920 BOOL start
, end
, Ret
= TRUE
;
925 /* FIXME: This function should check for all possible error returns */
926 /* FIXME: Do we have to respect newStroke? */
930 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
931 if (!pPath
) return FALSE
;
934 clockwise
= ((direction
== AD_CLOCKWISE
) !=0 );
936 clockwise
= ((dc
->dclevel
.flPath
& DCPATH_CLOCKWISE
) != 0);
938 /* Check for zero height / width */
939 /* FIXME: Only in GM_COMPATIBLE? */
940 if (x1
== x2
|| y1
== y2
)
945 /* Convert points to device coordinates */
946 corners
[0].x
= (FLOAT
)x1
;
947 corners
[0].y
= (FLOAT
)y1
;
948 corners
[1].x
= (FLOAT
)x2
;
949 corners
[1].y
= (FLOAT
)y2
;
950 pointStart
.x
= (FLOAT
)xStart
;
951 pointStart
.y
= (FLOAT
)yStart
;
952 pointEnd
.x
= (FLOAT
)xEnd
;
953 pointEnd
.y
= (FLOAT
)yEnd
;
954 INTERNAL_LPTODP_FLOAT(dc
, corners
);
955 INTERNAL_LPTODP_FLOAT(dc
, corners
+ 1);
956 INTERNAL_LPTODP_FLOAT(dc
, &pointStart
);
957 INTERNAL_LPTODP_FLOAT(dc
, &pointEnd
);
959 /* Make sure first corner is top left and second corner is bottom right */
960 if (corners
[0].x
> corners
[1].x
)
963 corners
[0].x
= corners
[1].x
;
966 if (corners
[0].y
> corners
[1].y
)
969 corners
[0].y
= corners
[1].y
;
973 /* Compute start and end angle */
974 PATH_NormalizePoint(corners
, &pointStart
, &x
, &y
);
975 angleStart
= atan2(y
, x
);
976 PATH_NormalizePoint(corners
, &pointEnd
, &x
, &y
);
977 angleEnd
= atan2(y
, x
);
979 /* Make sure the end angle is "on the right side" of the start angle */
982 if (angleEnd
<= angleStart
)
984 angleEnd
+= 2 * M_PI
;
985 ASSERT(angleEnd
>= angleStart
);
990 if (angleEnd
>= angleStart
)
992 angleEnd
-= 2 * M_PI
;
993 ASSERT(angleEnd
<= angleStart
);
997 /* In GM_COMPATIBLE, don't include bottom and right edges */
998 if (dc
->pdcattr
->iGraphicsMode
== GM_COMPATIBLE
)
1004 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1005 if (lines
== GdiTypeArcTo
&& pPath
->newStroke
) // -1
1007 pPath
->newStroke
= FALSE
;
1008 IntGetCurrentPositionEx(dc
, &pointCurPos
);
1009 CoordLPtoDP(dc
, &pointCurPos
);
1010 if (!PATH_AddEntry(pPath
, &pointCurPos
, PT_MOVETO
))
1017 /* Add the arc to the path with one Bezier spline per quadrant that the
1023 /* Determine the start and end angles for this quadrant */
1026 angleStartQuadrant
= angleStart
;
1028 angleEndQuadrant
= (floor(angleStart
/ M_PI_2
) + 1.0) * M_PI_2
;
1030 angleEndQuadrant
= (ceil(angleStart
/ M_PI_2
) - 1.0) * M_PI_2
;
1034 angleStartQuadrant
= angleEndQuadrant
;
1036 angleEndQuadrant
+= M_PI_2
;
1038 angleEndQuadrant
-= M_PI_2
;
1041 /* Have we reached the last part of the arc? */
1042 if ((clockwise
&& angleEnd
< angleEndQuadrant
) ||
1043 (!clockwise
&& angleEnd
> angleEndQuadrant
))
1045 /* Adjust the end angle for this quadrant */
1046 angleEndQuadrant
= angleEnd
;
1050 /* Add the Bezier spline to the path */
1051 PATH_DoArcPart(pPath
,
1055 start
? (lines
== GdiTypeArcTo
? PT_LINETO
: PT_MOVETO
) : FALSE
); // -1
1060 if (lines
== GdiTypeArcTo
)
1062 update_current_pos( pPath
);
1064 else /* chord: close figure. pie: add line and close figure */
1065 if (lines
== GdiTypeChord
) // 1
1067 IntGdiCloseFigure(pPath
);
1069 else if (lines
== GdiTypePie
) // 2
1071 centre
.x
= (corners
[0].x
+ corners
[1].x
) / 2;
1072 centre
.y
= (corners
[0].y
+ corners
[1].y
) / 2;
1073 if (!PATH_AddEntry(pPath
, ¢re
, PT_LINETO
| PT_CLOSEFIGURE
))
1077 PATH_UnlockPath(pPath
);
1095 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1096 if (!pPath
) return FALSE
;
1098 ret
= add_log_points_new_stroke( dc
, pPath
, pts
, cbPoints
, PT_BEZIERTO
);
1100 PATH_UnlockPath(pPath
);
1118 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1119 if (!pPath
) return FALSE
;
1121 type
= add_log_points( dc
, pPath
, pts
, cbPoints
, PT_BEZIERTO
);
1122 if (!type
) return FALSE
;
1124 type
[0] = PT_MOVETO
;
1126 PATH_UnlockPath(pPath
);
1139 POINT orig_pos
, cur_pos
;
1140 ULONG i
, lastmove
= 0;
1142 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1143 if (!pPath
) return FALSE
;
1145 if (pPath
->state
!= PATH_Open
)
1147 PATH_UnlockPath(pPath
);
1151 for (i
= 0; i
< pPath
->numEntriesUsed
; i
++) if (pPath
->pFlags
[i
] == PT_MOVETO
) lastmove
= i
;
1152 orig_pos
= pPath
->pos
;
1154 IntGetCurrentPositionEx(dc
, &cur_pos
);
1156 DPRINT("PPD : Current pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1157 DPRINT("PPD : last %d pos X %d Y %d\n",lastmove
, pPath
->pPoints
[lastmove
].x
, pPath
->pPoints
[lastmove
].y
);
1160 for(i
= 0; i
< cbPoints
; i
++)
1165 pPath
->newStroke
= TRUE
;
1166 pPath
->pos
= pts
[i
];
1167 IntLPtoDP( dc
, &pPath
->pos
, 1);
1168 lastmove
= pPath
->numEntriesUsed
;
1171 case PT_LINETO
| PT_CLOSEFIGURE
:
1172 if (!add_log_points_new_stroke( dc
, pPath
, &pts
[i
], 1, PT_LINETO
))
1174 PATH_UnlockPath(pPath
);
1179 if ((i
+ 2 < cbPoints
) && (types
[i
+ 1] == PT_BEZIERTO
) &&
1180 (types
[i
+ 2] & ~PT_CLOSEFIGURE
) == PT_BEZIERTO
)
1182 if (!add_log_points_new_stroke( dc
, pPath
, &pts
[i
], 3, PT_BEZIERTO
))
1184 PATH_UnlockPath(pPath
);
1192 /* restore original position */
1193 pPath
->pos
= orig_pos
;
1195 DPRINT("PPD Bad : pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1197 IntGdiMoveToEx(dc
, cur_pos
.x
, cur_pos
.y
, NULL
);
1199 PATH_UnlockPath(pPath
);
1203 if (types
[i
] & PT_CLOSEFIGURE
)
1205 close_figure( pPath
);
1206 pPath
->pos
= pPath
->pPoints
[lastmove
];
1207 DPRINT("PPD close : pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
1210 PATH_UnlockPath(pPath
);
1228 if (cbPoints
< 1) return FALSE
;
1230 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1231 if (!pPath
) return FALSE
;
1233 ret
= add_log_points_new_stroke( dc
, pPath
, pts
, cbPoints
, PT_LINETO
);
1234 PATH_UnlockPath(pPath
);
1255 if (!polygons
) return FALSE
;
1257 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1258 if (!pPath
) return FALSE
;
1261 for (poly
= count
= 0; poly
< polygons
; poly
++)
1263 if (counts
[poly
] < 2)
1265 PATH_UnlockPath(pPath
);
1268 count
+= counts
[poly
];
1271 type
= add_log_points( dc
, pPath
, pts
, count
, PT_LINETO
);
1274 PATH_UnlockPath(pPath
);
1278 /* make the first point of each polyline a PT_MOVETO, and close the last one */
1279 for (poly
= 0; poly
< polygons
; type
+= counts
[poly
++])
1281 type
[0] = PT_MOVETO
;
1282 type
[counts
[poly
] - 1] = PT_LINETO
| PT_CLOSEFIGURE
;
1284 PATH_UnlockPath(pPath
);
1293 const DWORD
* counts
,
1297 ULONG poly
, point
, i
;
1305 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
1311 for (i
= 0, poly
= 0; poly
< polylines
; poly
++)
1313 for (point
= 0; point
< counts
[poly
]; point
++, i
++)
1316 CoordLPtoDP(dc
, &pt
);
1317 PATH_AddEntry(pPath
, &pt
, (point
== 0) ? PT_MOVETO
: PT_LINETO
);
1320 DPRINT("PATH_PolyPolyline end count %d\n",pPath
->numEntriesUsed
);
1321 PATH_UnlockPath(pPath
);
1325 /* PATH_AddFlatBezier
1339 pts
= GDI_Bezier(pt
, 4, &no
);
1340 if (!pts
) return FALSE
;
1342 for (i
= 1; i
< no
; i
++)
1344 if (!(ret
= PATH_AddEntry(pPath
, &pts
[i
], (i
== no
- 1 && closed
) ? PT_LINETO
| PT_CLOSEFIGURE
: PT_LINETO
)))
1348 ExFreePoolWithTag(pts
, TAG_BEZIER
);
1354 * Replaces Beziers with line segments
1359 PATH_FlattenPath(PPATH pPath
)
1363 DPRINT("PATH_FlattenPath\n");
1364 if (!(newPath
= PATH_CreatePath(pPath
->numEntriesUsed
))) return NULL
;
1366 for (srcpt
= 0; srcpt
< pPath
->numEntriesUsed
; srcpt
++)
1368 switch(pPath
->pFlags
[srcpt
] & ~PT_CLOSEFIGURE
)
1372 if (!PATH_AddEntry(newPath
, &pPath
->pPoints
[srcpt
], pPath
->pFlags
[srcpt
]))
1374 PATH_UnlockPath(newPath
);
1375 PATH_Delete(newPath
->BaseObject
.hHmgr
);
1380 if(!PATH_AddFlatBezier(newPath
, &pPath
->pPoints
[srcpt
- 1], pPath
->pFlags
[srcpt
+ 2] & PT_CLOSEFIGURE
))
1382 PATH_UnlockPath(newPath
);
1383 PATH_Delete(newPath
->BaseObject
.hHmgr
);
1390 DPRINT("PATH_FlattenPath good\n");
1391 newPath
->state
= pPath
->state
;
1395 /* PATH_PathToRegion
1397 * Creates a region from the specified path using the specified polygon
1398 * filling mode. The path is left unchanged. A handle to the region that
1399 * was created is stored in *pHrgn.
1408 int i
, pos
, polygons
;
1412 if (!pPath
->numEntriesUsed
) return FALSE
;
1414 counts
= ExAllocatePoolWithTag(PagedPool
, (pPath
->numEntriesUsed
/ 2) * sizeof(counts
), TAG_PATH
);
1417 DPRINT1("Failed to allocate %lu strokes\n", (pPath
->numEntriesUsed
/ 2) * sizeof(*counts
));
1418 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1423 ASSERT( pPath
->pFlags
[0] == PT_MOVETO
);
1424 for (i
= 1; i
< pPath
->numEntriesUsed
; i
++)
1426 if (pPath
->pFlags
[i
] != PT_MOVETO
) continue;
1427 counts
[polygons
++] = i
- pos
;
1430 if (i
> pos
+ 1) counts
[polygons
++] = i
- pos
;
1432 ASSERT( polygons
<= pPath
->numEntriesUsed
/ 2 );
1434 /* Fill the region with the strokes */
1435 Ret
= REGION_SetPolyPolygonRgn(Rgn
,
1442 DPRINT1("REGION_SetPolyPolygonRgn failed\n");
1445 ExFreePoolWithTag(counts
, TAG_PATH
);
1453 * You can play with this as long as you like, but if you break Area.exe the purge will Begain on Path!!!
1462 return PATH_FillPathEx(dc
, pPath
, NULL
);
1472 INT mapMode
, graphicsMode
;
1473 SIZE ptViewportExt
, ptWindowExt
;
1474 POINTL ptViewportOrg
, ptWindowOrg
;
1477 PDC_ATTR pdcattr
= dc
->pdcattr
;
1479 /* Allocate a temporary region */
1480 Rgn
= IntSysCreateRectpRgn(0, 0, 0, 0);
1483 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1487 if (!PATH_PathToRegion(pPath
, pdcattr
->jFillMode
, Rgn
))
1489 DPRINT("PFP : Fail P2R\n");
1490 /* EngSetLastError ? */
1495 /* Since PaintRgn interprets the region as being in logical coordinates
1496 * but the points we store for the path are already in device
1497 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
1498 * Using SaveDC to save information about the mapping mode / world
1499 * transform would be easier but would require more overhead, especially
1500 * now that SaveDC saves the current path.
1503 /* Save the information about the old mapping mode */
1504 mapMode
= pdcattr
->iMapMode
;
1505 ptViewportExt
= pdcattr
->szlViewportExt
;
1506 ptViewportOrg
= pdcattr
->ptlViewportOrg
;
1507 ptWindowExt
= pdcattr
->szlWindowExt
;
1508 ptWindowOrg
= pdcattr
->ptlWindowOrg
;
1510 /* Save world transform
1511 * NB: The Windows documentation on world transforms would lead one to
1512 * believe that this has to be done only in GM_ADVANCED; however, my
1513 * tests show that resetting the graphics mode to GM_COMPATIBLE does
1514 * not reset the world transform.
1516 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1519 IntGdiSetMapMode(dc
, MM_TEXT
);
1520 pdcattr
->ptlViewportOrg
.x
= 0;
1521 pdcattr
->ptlViewportOrg
.y
= 0;
1522 pdcattr
->ptlWindowOrg
.x
= 0;
1523 pdcattr
->ptlWindowOrg
.y
= 0;
1525 graphicsMode
= pdcattr
->iGraphicsMode
;
1526 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1527 GreModifyWorldTransform(dc
, &xform
, MWT_IDENTITY
);
1528 pdcattr
->iGraphicsMode
= graphicsMode
;
1530 /* Paint the region */
1531 IntGdiFillRgn(dc
, Rgn
, pbrFill
);
1533 /* Restore the old mapping mode */
1534 IntGdiSetMapMode(dc
, mapMode
);
1535 pdcattr
->szlViewportExt
= ptViewportExt
;
1536 pdcattr
->ptlViewportOrg
= ptViewportOrg
;
1537 pdcattr
->szlWindowExt
= ptWindowExt
;
1538 pdcattr
->ptlWindowOrg
= ptWindowOrg
;
1540 /* Go to GM_ADVANCED temporarily to restore the world transform */
1541 graphicsMode
= pdcattr
->iGraphicsMode
;
1542 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1543 GreModifyWorldTransform(dc
, &xform
, MWT_SET
);
1544 pdcattr
->iGraphicsMode
= graphicsMode
;
1556 INT nLinePts
, nAlloc
;
1557 POINT
*pLinePts
= NULL
;
1558 POINT ptViewportOrg
, ptWindowOrg
;
1559 SIZE szViewportExt
, szWindowExt
;
1560 DWORD mapMode
, graphicsMode
;
1562 PDC_ATTR pdcattr
= dc
->pdcattr
;
1564 DPRINT("Enter %s\n", __FUNCTION__
);
1566 /* Save the mapping mode info */
1567 mapMode
= pdcattr
->iMapMode
;
1569 szViewportExt
= *DC_pszlViewportExt(dc
);
1570 ptViewportOrg
= dc
->pdcattr
->ptlViewportOrg
;
1571 szWindowExt
= dc
->pdcattr
->szlWindowExt
;
1572 ptWindowOrg
= dc
->pdcattr
->ptlWindowOrg
;
1574 MatrixS2XForm(&xform
, &dc
->pdcattr
->mxWorldToPage
);
1577 pdcattr
->iMapMode
= MM_TEXT
;
1578 pdcattr
->ptlViewportOrg
.x
= 0;
1579 pdcattr
->ptlViewportOrg
.y
= 0;
1580 pdcattr
->ptlWindowOrg
.x
= 0;
1581 pdcattr
->ptlWindowOrg
.y
= 0;
1582 graphicsMode
= pdcattr
->iGraphicsMode
;
1583 pdcattr
->iGraphicsMode
= GM_ADVANCED
;
1584 GreModifyWorldTransform(dc
, (XFORML
*)&xform
, MWT_IDENTITY
);
1585 pdcattr
->iGraphicsMode
= graphicsMode
;
1587 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1588 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1589 * space in case we get one to keep the number of reallocations small. */
1590 nAlloc
= pPath
->numEntriesUsed
+ 1 + 300;
1591 pLinePts
= ExAllocatePoolWithTag(PagedPool
, nAlloc
* sizeof(POINT
), TAG_PATH
);
1594 DPRINT1("Can't allocate pool!\n");
1595 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1600 for (i
= 0; i
< pPath
->numEntriesUsed
; i
++)
1602 if ((i
== 0 || (pPath
->pFlags
[i
- 1] & PT_CLOSEFIGURE
))
1603 && (pPath
->pFlags
[i
] != PT_MOVETO
))
1605 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1606 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1607 (INT
)pPath
->pFlags
[i
]);
1611 switch(pPath
->pFlags
[i
])
1614 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1615 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1616 if (nLinePts
>= 2) IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1618 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1621 case (PT_LINETO
| PT_CLOSEFIGURE
):
1622 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1623 pPath
->pPoints
[i
].x
, pPath
->pPoints
[i
].y
);
1624 pLinePts
[nLinePts
++] = pPath
->pPoints
[i
];
1627 DPRINT("Got PT_BEZIERTO\n");
1628 if (pPath
->pFlags
[i
+ 1] != PT_BEZIERTO
||
1629 (pPath
->pFlags
[i
+ 2] & ~PT_CLOSEFIGURE
) != PT_BEZIERTO
)
1631 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1637 INT nBzrPts
, nMinAlloc
;
1638 POINT
*pBzrPts
= GDI_Bezier(&pPath
->pPoints
[i
- 1], 4, &nBzrPts
);
1639 /* Make sure we have allocated enough memory for the lines of
1640 * this bezier and the rest of the path, assuming we won't get
1641 * another one (since we won't reallocate again then). */
1642 nMinAlloc
= nLinePts
+ (pPath
->numEntriesUsed
- i
) + nBzrPts
;
1643 if (nAlloc
< nMinAlloc
)
1645 // Reallocate memory
1647 POINT
*Realloc
= NULL
;
1648 nAlloc
= nMinAlloc
* 2;
1650 Realloc
= ExAllocatePoolWithTag(PagedPool
,
1651 nAlloc
* sizeof(POINT
),
1656 DPRINT1("Can't allocate pool!\n");
1657 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1661 memcpy(Realloc
, pLinePts
, nLinePts
* sizeof(POINT
));
1662 ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1665 memcpy(&pLinePts
[nLinePts
], &pBzrPts
[1], (nBzrPts
- 1) * sizeof(POINT
));
1666 nLinePts
+= nBzrPts
- 1;
1667 ExFreePoolWithTag(pBzrPts
, TAG_BEZIER
);
1672 DPRINT1("Got path flag %d (not supported)\n", (INT
)pPath
->pFlags
[i
]);
1676 if (pPath
->pFlags
[i
] & PT_CLOSEFIGURE
)
1678 pLinePts
[nLinePts
++] = pLinePts
[0];
1682 IntGdiPolyline(dc
, pLinePts
, nLinePts
);
1687 if (pLinePts
) ExFreePoolWithTag(pLinePts
, TAG_PATH
);
1689 /* Restore the old mapping mode */
1690 pdcattr
->iMapMode
= mapMode
;
1691 pdcattr
->szlWindowExt
.cx
= szWindowExt
.cx
;
1692 pdcattr
->szlWindowExt
.cy
= szWindowExt
.cy
;
1693 pdcattr
->ptlWindowOrg
.x
= ptWindowOrg
.x
;
1694 pdcattr
->ptlWindowOrg
.y
= ptWindowOrg
.y
;
1696 pdcattr
->szlViewportExt
.cx
= szViewportExt
.cx
;
1697 pdcattr
->szlViewportExt
.cy
= szViewportExt
.cy
;
1698 pdcattr
->ptlViewportOrg
.x
= ptViewportOrg
.x
;
1699 pdcattr
->ptlViewportOrg
.y
= ptViewportOrg
.y
;
1701 /* Restore the world transform */
1702 XForm2MatrixS(&dc
->pdcattr
->mxWorldToPage
, &xform
);
1704 /* If we've moved the current point then get its new position
1705 which will be in device (MM_TEXT) co-ords, convert it to
1706 logical co-ords and re-set it. This basically updates
1707 dc->CurPosX|Y so that their values are in the correct mapping
1713 IntGetCurrentPositionEx(dc
, &pt
);
1714 IntDPtoLP(dc
, &pt
, 1);
1715 IntGdiMoveToEx(dc
, pt
.x
, pt
.y
, NULL
);
1717 DPRINT("Leave %s, ret=%d\n", __FUNCTION__
, ret
);
1721 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1724 IntGdiWidenPath(PPATH pPath
, UINT penWidth
, UINT penStyle
, FLOAT eMiterLimit
)
1726 INT i
, j
, numStrokes
, numOldStrokes
, penWidthIn
, penWidthOut
;
1727 PPATH flat_path
, pNewPath
, *pStrokes
= NULL
, *pOldStrokes
, pUpPath
, pDownPath
;
1729 DWORD joint
, endcap
;
1731 endcap
= (PS_ENDCAP_MASK
& penStyle
);
1732 joint
= (PS_JOIN_MASK
& penStyle
);
1734 if (!(flat_path
= PATH_FlattenPath(pPath
)))
1736 DPRINT1("PATH_FlattenPath\n");
1740 penWidthIn
= penWidth
/ 2;
1741 penWidthOut
= penWidth
/ 2;
1742 if (penWidthIn
+ penWidthOut
< penWidth
)
1747 for (i
= 0, j
= 0; i
< flat_path
->numEntriesUsed
; i
++, j
++)
1750 if ((i
== 0 || (flat_path
->pFlags
[i
- 1] & PT_CLOSEFIGURE
)) &&
1751 (flat_path
->pFlags
[i
] != PT_MOVETO
))
1753 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1754 i
== 0 ? "as first point" : "after PT_CLOSEFIGURE",
1755 flat_path
->pFlags
[i
]);
1757 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1758 PATH_UnlockPath(flat_path
);
1759 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1762 switch(flat_path
->pFlags
[i
])
1767 pStrokes
[numStrokes
- 1]->state
= PATH_Closed
;
1769 numOldStrokes
= numStrokes
;
1772 if (numStrokes
== 1)
1773 pStrokes
= ExAllocatePoolWithTag(PagedPool
, sizeof(*pStrokes
), TAG_PATH
);
1776 pOldStrokes
= pStrokes
; // Save old pointer.
1777 pStrokes
= ExAllocatePoolWithTag(PagedPool
, numStrokes
* sizeof(*pStrokes
), TAG_PATH
);
1780 ExFreePoolWithTag(pOldStrokes
, TAG_PATH
);
1781 PATH_UnlockPath(flat_path
);
1782 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1785 RtlCopyMemory(pStrokes
, pOldStrokes
, numOldStrokes
* sizeof(PPATH
));
1786 ExFreePoolWithTag(pOldStrokes
, TAG_PATH
); // Free old pointer.
1790 PATH_UnlockPath(flat_path
);
1791 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1794 pStrokes
[numStrokes
- 1] = ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1795 if (!pStrokes
[numStrokes
- 1])
1797 ASSERT(FALSE
); // FIXME
1799 PATH_InitGdiPath(pStrokes
[numStrokes
- 1]);
1800 pStrokes
[numStrokes
- 1]->state
= PATH_Open
;
1802 case (PT_LINETO
| PT_CLOSEFIGURE
):
1803 point
.x
= flat_path
->pPoints
[i
].x
;
1804 point
.y
= flat_path
->pPoints
[i
].y
;
1805 PATH_AddEntry(pStrokes
[numStrokes
- 1], &point
, flat_path
->pFlags
[i
]);
1808 /* Should never happen because of the FlattenPath call */
1809 DPRINT1("Should never happen\n");
1812 DPRINT1("Got path flag %c\n", flat_path
->pFlags
[i
]);
1814 ExFreePoolWithTag(pStrokes
, TAG_PATH
);
1815 PATH_UnlockPath(flat_path
);
1816 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
1821 pNewPath
= PATH_CreatePath( flat_path
->numEntriesUsed
);
1823 for (i
= 0; i
< numStrokes
; i
++)
1825 pUpPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1826 PATH_InitGdiPath(pUpPath
);
1827 pUpPath
->state
= PATH_Open
;
1828 pDownPath
= ExAllocatePoolWithTag(PagedPool
, sizeof(PATH
), TAG_PATH
);
1829 PATH_InitGdiPath(pDownPath
);
1830 pDownPath
->state
= PATH_Open
;
1832 for (j
= 0; j
< pStrokes
[i
]->numEntriesUsed
; j
++)
1834 /* Beginning or end of the path if not closed */
1835 if ((!(pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
)) && (j
== 0 || j
== pStrokes
[i
]->numEntriesUsed
- 1))
1837 /* Compute segment angle */
1838 double xo
, yo
, xa
, ya
, theta
;
1840 FLOAT_POINT corners
[2];
1843 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1844 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1845 xa
= pStrokes
[i
]->pPoints
[1].x
;
1846 ya
= pStrokes
[i
]->pPoints
[1].y
;
1850 xa
= pStrokes
[i
]->pPoints
[j
- 1].x
;
1851 ya
= pStrokes
[i
]->pPoints
[j
- 1].y
;
1852 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1853 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1855 theta
= atan2(ya
- yo
, xa
- xo
);
1858 case PS_ENDCAP_SQUARE
:
1859 pt
.x
= xo
+ round(sqrt(2) * penWidthOut
* cos(M_PI_4
+ theta
));
1860 pt
.y
= yo
+ round(sqrt(2) * penWidthOut
* sin(M_PI_4
+ theta
));
1861 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1862 pt
.x
= xo
+ round(sqrt(2) * penWidthIn
* cos(- M_PI_4
+ theta
));
1863 pt
.y
= yo
+ round(sqrt(2) * penWidthIn
* sin(- M_PI_4
+ theta
));
1864 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1866 case PS_ENDCAP_FLAT
:
1867 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
1868 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
1869 PATH_AddEntry(pUpPath
, &pt
, (j
== 0 ? PT_MOVETO
: PT_LINETO
));
1870 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
1871 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
1872 PATH_AddEntry(pUpPath
, &pt
, PT_LINETO
);
1874 case PS_ENDCAP_ROUND
:
1876 corners
[0].x
= xo
- penWidthIn
;
1877 corners
[0].y
= yo
- penWidthIn
;
1878 corners
[1].x
= xo
+ penWidthOut
;
1879 corners
[1].y
= yo
+ penWidthOut
;
1880 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI_2
, theta
+ 3 * M_PI_4
, (j
== 0 ? PT_MOVETO
: FALSE
));
1881 PATH_DoArcPart(pUpPath
, corners
, theta
+ 3 * M_PI_4
, theta
+ M_PI
, FALSE
);
1882 PATH_DoArcPart(pUpPath
, corners
, theta
+ M_PI
, theta
+ 5 * M_PI_4
, FALSE
);
1883 PATH_DoArcPart(pUpPath
, corners
, theta
+ 5 * M_PI_4
, theta
+ 3 * M_PI_2
, FALSE
);
1887 /* Corpse of the path */
1892 double xa
, ya
, xb
, yb
, xo
, yo
;
1893 double alpha
, theta
, miterWidth
;
1894 DWORD _joint
= joint
;
1896 PPATH pInsidePath
, pOutsidePath
;
1897 if (j
> 0 && j
< pStrokes
[i
]->numEntriesUsed
- 1)
1904 previous
= pStrokes
[i
]->numEntriesUsed
- 1;
1912 xo
= pStrokes
[i
]->pPoints
[j
].x
;
1913 yo
= pStrokes
[i
]->pPoints
[j
].y
;
1914 xa
= pStrokes
[i
]->pPoints
[previous
].x
;
1915 ya
= pStrokes
[i
]->pPoints
[previous
].y
;
1916 xb
= pStrokes
[i
]->pPoints
[next
].x
;
1917 yb
= pStrokes
[i
]->pPoints
[next
].y
;
1918 theta
= atan2(yo
- ya
, xo
- xa
);
1919 alpha
= atan2(yb
- yo
, xb
- xo
) - theta
;
1920 if (alpha
> 0) alpha
-= M_PI
;
1922 if (_joint
== PS_JOIN_MITER
&& eMiterLimit
< fabs(1 / sin(alpha
/ 2)))
1924 _joint
= PS_JOIN_BEVEL
;
1928 pInsidePath
= pUpPath
;
1929 pOutsidePath
= pDownPath
;
1933 pInsidePath
= pDownPath
;
1934 pOutsidePath
= pUpPath
;
1940 /* Inside angle points */
1943 pt
.x
= xo
- round(penWidthIn
* cos(theta
+ M_PI_2
));
1944 pt
.y
= yo
- round(penWidthIn
* sin(theta
+ M_PI_2
));
1948 pt
.x
= xo
+ round(penWidthIn
* cos(theta
+ M_PI_2
));
1949 pt
.y
= yo
+ round(penWidthIn
* sin(theta
+ M_PI_2
));
1951 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
1954 pt
.x
= xo
+ round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
1955 pt
.y
= yo
+ round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
1959 pt
.x
= xo
- round(penWidthIn
* cos(M_PI_2
+ alpha
+ theta
));
1960 pt
.y
= yo
- round(penWidthIn
* sin(M_PI_2
+ alpha
+ theta
));
1962 PATH_AddEntry(pInsidePath
, &pt
, PT_LINETO
);
1963 /* Outside angle point */
1966 case PS_JOIN_MITER
:
1967 miterWidth
= fabs(penWidthOut
/ cos(M_PI_2
- fabs(alpha
) / 2));
1968 pt
.x
= xo
+ round(miterWidth
* cos(theta
+ alpha
/ 2));
1969 pt
.y
= yo
+ round(miterWidth
* sin(theta
+ alpha
/ 2));
1970 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
1972 case PS_JOIN_BEVEL
:
1975 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
1976 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
1980 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
1981 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
1983 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
1986 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
1987 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
1991 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
1992 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
1994 PATH_AddEntry(pOutsidePath
, &pt
, PT_LINETO
);
1996 case PS_JOIN_ROUND
:
2000 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ M_PI_2
));
2001 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ M_PI_2
));
2005 pt
.x
= xo
- round(penWidthOut
* cos(theta
+ M_PI_2
));
2006 pt
.y
= yo
- round(penWidthOut
* sin(theta
+ M_PI_2
));
2008 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2009 pt
.x
= xo
+ round(penWidthOut
* cos(theta
+ alpha
/ 2));
2010 pt
.y
= yo
+ round(penWidthOut
* sin(theta
+ alpha
/ 2));
2011 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2014 pt
.x
= xo
- round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2015 pt
.y
= yo
- round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2019 pt
.x
= xo
+ round(penWidthOut
* cos(M_PI_2
+ alpha
+ theta
));
2020 pt
.y
= yo
+ round(penWidthOut
* sin(M_PI_2
+ alpha
+ theta
));
2022 PATH_AddEntry(pOutsidePath
, &pt
, PT_BEZIERTO
);
2027 type
= add_points( pNewPath
, pUpPath
->pPoints
, pUpPath
->numEntriesUsed
, PT_LINETO
);
2028 type
[0] = PT_MOVETO
;
2029 reverse_points( pDownPath
->pPoints
, pDownPath
->numEntriesUsed
);
2030 type
= add_points( pNewPath
, pDownPath
->pPoints
, pDownPath
->numEntriesUsed
, PT_LINETO
);
2031 if (pStrokes
[i
]->pFlags
[pStrokes
[i
]->numEntriesUsed
- 1] & PT_CLOSEFIGURE
) type
[0] = PT_MOVETO
;
2033 PATH_DestroyGdiPath(pStrokes
[i
]);
2034 ExFreePoolWithTag(pStrokes
[i
], TAG_PATH
);
2035 PATH_DestroyGdiPath(pUpPath
);
2036 ExFreePoolWithTag(pUpPath
, TAG_PATH
);
2037 PATH_DestroyGdiPath(pDownPath
);
2038 ExFreePoolWithTag(pDownPath
, TAG_PATH
);
2040 if (pStrokes
) ExFreePoolWithTag(pStrokes
, TAG_PATH
);
2042 PATH_UnlockPath(flat_path
);
2043 PATH_Delete(flat_path
->BaseObject
.hHmgr
);
2044 pNewPath
->state
= PATH_Closed
;
2045 PATH_UnlockPath(pNewPath
);
2052 PATH_WidenPath(DC
*dc
)
2055 UINT penWidth
, penStyle
;
2057 PPATH pPath
, pNewPath
;
2059 PDC_ATTR pdcattr
= dc
->pdcattr
;
2061 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2064 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
2068 if (pPath
->state
!= PATH_Closed
)
2071 PATH_UnlockPath(pPath
);
2072 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2076 size
= GreGetObject(pdcattr
->hpen
, 0, NULL
);
2080 PATH_UnlockPath(pPath
);
2081 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2085 elp
= ExAllocatePoolWithTag(PagedPool
, size
, TAG_PATH
);
2089 PATH_UnlockPath(pPath
);
2090 EngSetLastError(ERROR_OUTOFMEMORY
);
2094 GreGetObject(pdcattr
->hpen
, size
, elp
);
2096 obj_type
= GDI_HANDLE_GET_TYPE(pdcattr
->hpen
);
2097 if (obj_type
== GDI_OBJECT_TYPE_PEN
)
2099 penStyle
= ((LOGPEN
*)elp
)->lopnStyle
;
2101 else if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
)
2103 penStyle
= elp
->elpPenStyle
;
2108 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2109 ExFreePoolWithTag(elp
, TAG_PATH
);
2110 PATH_UnlockPath(pPath
);
2114 penWidth
= elp
->elpWidth
;
2115 ExFreePoolWithTag(elp
, TAG_PATH
);
2117 /* The function cannot apply to cosmetic pens */
2118 if (obj_type
== GDI_OBJECT_TYPE_EXTPEN
&&
2119 (PS_TYPE_MASK
& penStyle
) == PS_COSMETIC
)
2122 PATH_UnlockPath(pPath
);
2123 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2127 pNewPath
= IntGdiWidenPath(pPath
, penWidth
, penStyle
, dc
->dclevel
.laPath
.eMiterLimit
);
2128 PATH_UnlockPath(pPath
);
2132 static inline INT
int_from_fixed(FIXED f
)
2134 return (f
.fract
>= 0x8000) ? (f
.value
+ 1) : f
.value
;
2137 /**********************************************************************
2140 * Internally used by PATH_add_outline
2154 PATH_AddEntry(pPath
, &lppt
[1], PT_LINETO
);
2158 add_points( pPath
, lppt
, 3, PT_BEZIERTO
);
2171 pt
[1] = lppt
[i
+ 1];
2172 pt
[2].x
= (lppt
[i
+ 2].x
+ lppt
[i
+ 1].x
) / 2;
2173 pt
[2].y
= (lppt
[i
+ 2].y
+ lppt
[i
+ 1].y
) / 2;
2174 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2180 pt
[1] = lppt
[i
+ 1];
2181 pt
[2] = lppt
[i
+ 2];
2182 add_points( pPath
, pt
, 3, PT_BEZIERTO
);
2194 TTPOLYGONHEADER
*header
,
2197 TTPOLYGONHEADER
*start
;
2199 BOOL bResult
= FALSE
;
2203 while ((char *)header
< (char *)start
+ size
)
2207 if (header
->dwType
!= TT_POLYGON_TYPE
)
2209 DPRINT1("Unknown header type %lu\n", header
->dwType
);
2213 pt
.x
= x
+ int_from_fixed(header
->pfxStart
.x
);
2214 pt
.y
= y
- int_from_fixed(header
->pfxStart
.y
);
2215 PATH_AddEntry(pPath
, &pt
, PT_MOVETO
);
2217 curve
= (TTPOLYCURVE
*)(header
+ 1);
2219 while ((char *)curve
< (char *)header
+ header
->cb
)
2221 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2223 switch(curve
->wType
)
2229 for (i
= 0; i
< curve
->cpfx
; i
++)
2231 pt
.x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2232 pt
.y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2233 PATH_AddEntry(pPath
, &pt
, PT_LINETO
);
2238 case TT_PRIM_QSPLINE
:
2239 case TT_PRIM_CSPLINE
:
2243 POINT
*pts
= ExAllocatePoolWithTag(PagedPool
, (curve
->cpfx
+ 1) * sizeof(POINT
), TAG_PATH
);
2245 if (!pts
) goto cleanup
;
2247 ptfx
= *(POINTFX
*)((char *)curve
- sizeof(POINTFX
));
2249 pts
[0].x
= x
+ int_from_fixed(ptfx
.x
);
2250 pts
[0].y
= y
- int_from_fixed(ptfx
.y
);
2252 for (i
= 0; i
< curve
->cpfx
; i
++)
2254 pts
[i
+ 1].x
= x
+ int_from_fixed(curve
->apfx
[i
].x
);
2255 pts
[i
+ 1].y
= y
- int_from_fixed(curve
->apfx
[i
].y
);
2258 PATH_BezierTo(pPath
, pts
, curve
->cpfx
+ 1);
2260 ExFreePoolWithTag(pts
, TAG_PATH
);
2265 DPRINT1("Unknown curve type %04x\n", curve
->wType
);
2269 curve
= (TTPOLYCURVE
*)&curve
->apfx
[curve
->cpfx
];
2271 header
= (TTPOLYGONHEADER
*)((char *)header
+ header
->cb
);
2277 IntGdiCloseFigure(pPath
);
2281 /**********************************************************************
2297 unsigned int idx
, ggo_flags
= GGO_NATIVE
;
2298 POINT offset
= {0, 0};
2300 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2306 if (pPath
->state
!= PATH_Open
)
2308 DPRINT1("PATH_ExtTextOut not open\n");
2312 if (!count
) return TRUE
;
2313 if (flags
& ETO_GLYPH_INDEX
) ggo_flags
|= GGO_GLYPH_INDEX
;
2315 for (idx
= 0; idx
< count
; idx
++)
2317 MAT2 identity
= { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2322 dwSize
= ftGdiGetGlyphOutline(dc
,
2330 if (dwSize
== GDI_ERROR
)
2332 PATH_UnlockPath(pPath
);
2336 /* Add outline only if char is printable */
2339 outline
= ExAllocatePoolWithTag(PagedPool
, dwSize
, TAG_PATH
);
2342 PATH_UnlockPath(pPath
);
2346 ftGdiGetGlyphOutline(dc
,
2355 PATH_add_outline(dc
, pPath
, x
+ offset
.x
, y
+ offset
.y
, outline
, dwSize
);
2357 ExFreePoolWithTag(outline
, TAG_PATH
);
2362 if (flags
& ETO_PDY
)
2364 offset
.x
+= dx
[idx
* 2];
2365 offset
.y
+= dx
[idx
* 2 + 1];
2368 offset
.x
+= dx
[idx
];
2372 offset
.x
+= gm
.gmCellIncX
;
2373 offset
.y
+= gm
.gmCellIncY
;
2376 PATH_UnlockPath(pPath
);
2381 /***********************************************************************
2382 * Exported functions
2387 NtGdiAbortPath(HDC hDC
)
2389 PDC dc
= DC_LockDc(hDC
);
2392 EngSetLastError(ERROR_INVALID_HANDLE
);
2396 if (!dc
->dclevel
.hPath
)
2402 if (!PATH_Delete(dc
->dclevel
.hPath
))
2408 dc
->dclevel
.hPath
= 0;
2409 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2417 NtGdiBeginPath(HDC hDC
)
2422 dc
= DC_LockDc(hDC
);
2425 EngSetLastError(ERROR_INVALID_HANDLE
);
2429 /* If path is already open, do nothing. Check if not Save DC state */
2430 if ((dc
->dclevel
.flPath
& DCPATH_ACTIVE
) && !(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2436 if (dc
->dclevel
.hPath
)
2438 DPRINT("BeginPath 1 0x%p\n", dc
->dclevel
.hPath
);
2439 if (!(dc
->dclevel
.flPath
& DCPATH_SAVE
))
2441 // Remove previous handle.
2442 if (!PATH_Delete(dc
->dclevel
.hPath
))
2450 // Clear flags and Handle.
2451 dc
->dclevel
.flPath
&= ~(DCPATH_SAVE
| DCPATH_ACTIVE
);
2452 dc
->dclevel
.hPath
= NULL
;
2455 pPath
= PATH_CreatePath(NUM_ENTRIES_INITIAL
);
2456 dc
->dclevel
.flPath
|= DCPATH_ACTIVE
; // Set active ASAP!
2457 dc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
2458 IntGetCurrentPositionEx(dc
, &pPath
->pos
);
2459 IntLPtoDP( dc
, &pPath
->pos
, 1 );
2460 DPRINT("BP : Current pos X %d Y %d\n",pPath
->pos
.x
, pPath
->pos
.y
);
2461 PATH_UnlockPath(pPath
);
2473 NtGdiCloseFigure(HDC hDC
)
2475 BOOL Ret
= FALSE
; // Default to failure
2479 DPRINT("Enter %s\n", __FUNCTION__
);
2481 pDc
= DC_LockDc(hDC
);
2484 EngSetLastError(ERROR_INVALID_PARAMETER
);
2488 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2495 if (pPath
->state
== PATH_Open
)
2497 IntGdiCloseFigure(pPath
);
2502 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2505 PATH_UnlockPath(pPath
);
2512 NtGdiEndPath(HDC hDC
)
2518 dc
= DC_LockDc(hDC
);
2521 EngSetLastError(ERROR_INVALID_HANDLE
);
2525 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2532 /* Check that path is currently being constructed */
2533 if ((pPath
->state
!= PATH_Open
) || !(dc
->dclevel
.flPath
& DCPATH_ACTIVE
))
2535 DPRINT("EndPath ERROR! 0x%p\n", dc
->dclevel
.hPath
);
2536 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2539 /* Set flag to indicate that path is finished */
2542 DPRINT("EndPath 0x%p\n", dc
->dclevel
.hPath
);
2543 pPath
->state
= PATH_Closed
;
2544 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2547 PATH_UnlockPath(pPath
);
2554 NtGdiFillPath(HDC hDC
)
2557 PPATH pPath
, pNewPath
;
2561 dc
= DC_LockDc(hDC
);
2564 EngSetLastError(ERROR_INVALID_PARAMETER
);
2568 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2575 DC_vPrepareDCsForBlit(dc
, NULL
, NULL
, NULL
);
2577 pdcattr
= dc
->pdcattr
;
2579 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2580 DC_vUpdateLineBrush(dc
);
2582 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2583 DC_vUpdateFillBrush(dc
);
2585 pNewPath
= PATH_FlattenPath(pPath
);
2587 if (pNewPath
->state
!= PATH_Closed
)
2589 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2591 else if (pNewPath
->numEntriesUsed
)
2593 ret
= PATH_FillPath(dc
, pNewPath
);
2597 PATH_UnlockPath(pNewPath
);
2598 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2600 PATH_UnlockPath(pPath
);
2601 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2602 dc
->dclevel
.hPath
= 0;
2603 dc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2605 DC_vFinishBlit(dc
, NULL
);
2612 NtGdiFlattenPath(HDC hDC
)
2616 PPATH pPath
, pNewPath
= NULL
;
2618 DPRINT("Enter %s\n", __FUNCTION__
);
2620 pDc
= DC_LockDc(hDC
);
2623 EngSetLastError(ERROR_INVALID_HANDLE
);
2627 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2630 EngSetLastError( ERROR_CAN_NOT_COMPLETE
);
2635 if (pPath
->state
== PATH_Closed
)
2637 pNewPath
= PATH_FlattenPath(pPath
);
2640 PATH_UnlockPath(pPath
);
2644 PATH_Delete(pDc
->dclevel
.hPath
);
2645 pDc
->dclevel
.hPath
= pNewPath
->BaseObject
.hHmgr
;
2646 PATH_UnlockPath(pNewPath
);
2654 _Success_(return != FALSE
)
2659 _Out_ PDWORD pdwOut
)
2662 BOOL bResult
= TRUE
;
2664 if (!(pDc
= DC_LockDc(hdc
)))
2666 EngSetLastError(ERROR_INVALID_PARAMETER
);
2672 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2673 *pdwOut
= pDc
->dclevel
.laPath
.eMiterLimit
;
2675 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2677 SetLastNtError(_SEH2_GetExceptionCode());
2698 DC
*dc
= DC_LockDc(hDC
);
2699 DPRINT("NtGdiGetPath start\n");
2702 DPRINT1("Can't lock dc!\n");
2703 EngSetLastError(ERROR_INVALID_PARAMETER
);
2707 pPath
= PATH_LockPath(dc
->dclevel
.hPath
);
2714 if (pPath
->state
!= PATH_Closed
)
2716 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2722 ret
= pPath
->numEntriesUsed
;
2724 else if (nSize
< pPath
->numEntriesUsed
)
2726 EngSetLastError(ERROR_INVALID_PARAMETER
);
2733 memcpy(Points
, pPath
->pPoints
, sizeof(POINT
)*pPath
->numEntriesUsed
);
2734 memcpy(Types
, pPath
->pFlags
, sizeof(BYTE
)*pPath
->numEntriesUsed
);
2736 /* Convert the points to logical coordinates */
2737 if (!GdiPathDPtoLP(dc
, Points
, pPath
->numEntriesUsed
))
2739 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW
);
2743 ret
= pPath
->numEntriesUsed
;
2745 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2747 SetLastNtError(_SEH2_GetExceptionCode());
2753 DPRINT("NtGdiGetPath exit %d\n",ret
);
2754 PATH_UnlockPath(pPath
);
2761 NtGdiPathToRegion(HDC hDC
)
2763 PPATH pPath
, pNewPath
;
2770 DPRINT("Enter %s\n", __FUNCTION__
);
2772 pDc
= DC_LockDc(hDC
);
2775 DPRINT("Failed to lock DC %p\n", hDC
);
2776 EngSetLastError(ERROR_INVALID_PARAMETER
);
2780 pdcattr
= pDc
->pdcattr
;
2782 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2785 DPRINT("Failed to lock DC path %p\n", pDc
->dclevel
.hPath
);
2790 if (pPath
->state
!= PATH_Closed
)
2792 // FIXME: Check that setlasterror is being called correctly
2793 DPRINT("Path is not closed!\n");
2794 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2798 /* Create the region and fill it with the path strokes */
2799 Rgn
= REGION_AllocUserRgnWithHandle(1);
2802 DPRINT("Failed to allocate a region\n");
2803 PATH_UnlockPath(pPath
);
2807 hrgnRval
= Rgn
->BaseObject
.hHmgr
;
2809 pNewPath
= PATH_FlattenPath(pPath
);
2811 Ret
= PATH_PathToRegion(pNewPath
, pdcattr
->jFillMode
, Rgn
);
2813 PATH_UnlockPath(pNewPath
);
2814 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2818 DPRINT("PATH_PathToRegion failed\n");
2823 REGION_UnlockRgn(Rgn
);
2826 PATH_UnlockPath(pPath
);
2827 PATH_Delete(pDc
->dclevel
.hPath
);
2828 pDc
->dclevel
.hPath
= NULL
;
2829 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2840 IN OUT OPTIONAL PDWORD pdwOut
)
2843 gxf_long worker
, worker1
;
2844 BOOL bResult
= TRUE
;
2846 if (!(pDc
= DC_LockDc(hdc
)))
2848 EngSetLastError(ERROR_INVALID_PARAMETER
);
2853 worker1
.f
= pDc
->dclevel
.laPath
.eMiterLimit
;
2854 pDc
->dclevel
.laPath
.eMiterLimit
= worker
.f
;
2860 ProbeForWrite(pdwOut
, sizeof(DWORD
), 1);
2861 *pdwOut
= worker1
.l
;
2863 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2865 SetLastNtError(_SEH2_GetExceptionCode());
2877 NtGdiStrokeAndFillPath(HDC hDC
)
2881 PPATH pPath
, pNewPath
;
2884 DPRINT("Enter %s\n", __FUNCTION__
);
2886 if (!(pDc
= DC_LockDc(hDC
)))
2888 EngSetLastError(ERROR_INVALID_PARAMETER
);
2891 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2898 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2900 pdcattr
= pDc
->pdcattr
;
2902 if (pdcattr
->ulDirty_
& (DIRTY_FILL
| DC_BRUSH_DIRTY
))
2903 DC_vUpdateFillBrush(pDc
);
2905 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2906 DC_vUpdateLineBrush(pDc
);
2908 pNewPath
= PATH_FlattenPath(pPath
);
2910 if (pNewPath
->state
!= PATH_Closed
)
2912 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2914 else if (pNewPath
->numEntriesUsed
)
2916 bRet
= PATH_FillPath(pDc
, pNewPath
);
2917 if (bRet
) bRet
= PATH_StrokePath(pDc
, pNewPath
);
2921 PATH_UnlockPath(pNewPath
);
2922 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2924 PATH_UnlockPath(pPath
);
2925 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2926 pDc
->dclevel
.hPath
= 0;
2927 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2929 DC_vFinishBlit(pDc
, NULL
);
2936 NtGdiStrokePath(HDC hDC
)
2940 PPATH pPath
, pNewPath
;
2943 DPRINT("Enter %s\n", __FUNCTION__
);
2945 if (!(pDc
= DC_LockDc(hDC
)))
2947 EngSetLastError(ERROR_INVALID_PARAMETER
);
2951 pPath
= PATH_LockPath(pDc
->dclevel
.hPath
);
2958 DC_vPrepareDCsForBlit(pDc
, NULL
, NULL
, NULL
);
2960 pdcattr
= pDc
->pdcattr
;
2962 if (pdcattr
->ulDirty_
& (DIRTY_LINE
| DC_PEN_DIRTY
))
2963 DC_vUpdateLineBrush(pDc
);
2965 pNewPath
= PATH_FlattenPath(pPath
);
2967 if (pNewPath
->state
!= PATH_Closed
)
2969 EngSetLastError(ERROR_CAN_NOT_COMPLETE
);
2971 else bRet
= PATH_StrokePath(pDc
, pNewPath
);
2973 PATH_UnlockPath(pNewPath
);
2974 PATH_Delete(pNewPath
->BaseObject
.hHmgr
);
2976 DC_vFinishBlit(pDc
, NULL
);
2978 PATH_UnlockPath(pPath
);
2979 PATH_Delete(pPath
->BaseObject
.hHmgr
);
2980 pDc
->dclevel
.hPath
= 0;
2981 pDc
->dclevel
.flPath
&= ~DCPATH_ACTIVE
;
2989 NtGdiWidenPath(HDC hDC
)
2993 PDC pdc
= DC_LockDc(hDC
);
2994 DPRINT("NtGdiWidenPat Enter\n");
2997 EngSetLastError(ERROR_INVALID_PARAMETER
);
3001 pPath
= PATH_WidenPath(pdc
);
3004 DPRINT("WindenPath New Path\n");
3005 PATH_Delete(pdc
->dclevel
.hPath
);
3006 pdc
->dclevel
.hPath
= pPath
->BaseObject
.hHmgr
;
3010 DPRINT("NtGdiWidenPat Ret %d\n",Ret
);