a3e2ea0cdd789916a78d3434515ee4017c03b1d4
[reactos.git] / win32ss / gdi / ntgdi / path.c
1 /*
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
7 * 1999 Huw D M Davies
8 * 2005 Dmitry Timoshkov
9 * 2018 Katayama Hirofumi MZ
10 */
11
12 #include <win32k.h>
13 #include <suppress.h>
14
15 #define NDEBUG
16 #include <debug.h>
17
18 #ifdef _MSC_VER
19 #pragma warning(disable:4244)
20 #endif
21
22 #define NUM_ENTRIES_INITIAL 16 /* Initial size of points / flags arrays */
23
24 #define GROW_FACTOR_NUMER 2 /* Numerator of grow factor for the array */
25 #define GROW_FACTOR_DENOM 1 /* Denominator of grow factor */
26
27 #if DBG
28 static int PathCount = 0;
29 #endif
30
31 /***********************************************************************
32 * Internal functions
33 */
34
35 PPATH FASTCALL
36 PATH_CreatePath(int count)
37 {
38 PPATH pPath = PATH_AllocPathWithHandle();
39
40 if (!pPath)
41 {
42 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
43 return NULL;
44 }
45
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.
50
51 /* Make sure that path is empty */
52 PATH_EmptyPath(pPath);
53
54 count = max( NUM_ENTRIES_INITIAL, count );
55
56 pPath->numEntriesAllocated = count;
57
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));
62
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;
68 #if DBG
69 PathCount++;
70 DPRINT("Create Path %d\n",PathCount);
71 #endif
72 return pPath;
73 }
74
75 /* PATH_DestroyGdiPath
76 *
77 * Destroys a GdiPath structure (frees the memory in the arrays).
78 */
79 VOID
80 FASTCALL
81 PATH_DestroyGdiPath(PPATH pPath)
82 {
83 ASSERT(pPath != NULL);
84
85 if (pPath->pPoints) ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
86 if (pPath->pFlags) ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
87 }
88
89 BOOL
90 FASTCALL
91 PATH_Delete(HPATH hPath)
92 {
93 PPATH pPath;
94 if (!hPath) return FALSE;
95 pPath = PATH_LockPath(hPath);
96 if (!pPath) return FALSE;
97 PATH_DestroyGdiPath(pPath);
98 GDIOBJ_vDeleteObject(&pPath->BaseObject);
99 #if DBG
100 PathCount--;
101 DPRINT("Delete Path %d\n",PathCount);
102 #endif
103 return TRUE;
104 }
105
106
107 VOID
108 FASTCALL
109 IntGdiCloseFigure(PPATH pPath)
110 {
111 ASSERT(pPath->state == PATH_Open);
112
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)
116 {
117 pPath->pFlags[pPath->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
118 pPath->newStroke = TRUE;
119 }
120 }
121
122 /* MSDN: This fails if the device coordinates exceed 27 bits, or if the converted
123 logical coordinates exceed 32 bits. */
124 BOOL
125 FASTCALL
126 GdiPathDPtoLP(
127 PDC pdc,
128 PPOINT ppt,
129 INT count)
130 {
131 XFORMOBJ xo;
132
133 XFORMOBJ_vInit(&xo, &pdc->pdcattr->mxDeviceToWorld);
134 return XFORMOBJ_bApplyXform(&xo, XF_LTOL, count, (PPOINTL)ppt, (PPOINTL)ppt);
135 }
136
137 /* PATH_InitGdiPath
138 *
139 * Initializes the GdiPath structure.
140 */
141 VOID
142 FASTCALL
143 PATH_InitGdiPath(
144 PPATH pPath)
145 {
146 ASSERT(pPath != NULL);
147
148 pPath->state = PATH_Null;
149 pPath->pPoints = NULL;
150 pPath->pFlags = NULL;
151 pPath->numEntriesUsed = 0;
152 pPath->numEntriesAllocated = 0;
153 }
154
155 /* PATH_AssignGdiPath
156 *
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.
164 */
165 BOOL
166 FASTCALL
167 PATH_AssignGdiPath(
168 PPATH pPathDest,
169 const PPATH pPathSrc)
170 {
171 ASSERT(pPathDest != NULL && pPathSrc != NULL);
172
173 /* Make sure destination arrays are big enough */
174 if (!PATH_ReserveEntries(pPathDest, pPathSrc->numEntriesUsed))
175 return FALSE;
176
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);
180
181 pPathDest->pos = pPathSrc->pos;
182 pPathDest->state = pPathSrc->state;
183 pPathDest->numEntriesUsed = pPathSrc->numEntriesUsed;
184 pPathDest->newStroke = pPathSrc->newStroke;
185 return TRUE;
186 }
187
188 BOOL PATH_SavePath( DC *dst, DC *src )
189 {
190 PPATH pdstPath, psrcPath = PATH_LockPath(src->dclevel.hPath);
191 DPRINT("PATH_SavePath\n");
192 if (psrcPath)
193 {
194 DPRINT("PATH_SavePath 1\n");
195
196 pdstPath = PATH_CreatePath(psrcPath->numEntriesAllocated);
197
198 dst->dclevel.flPath = src->dclevel.flPath;
199
200 dst->dclevel.hPath = pdstPath->BaseObject.hHmgr;
201
202 PATH_AssignGdiPath(pdstPath, psrcPath);
203
204 PATH_UnlockPath(pdstPath);
205 PATH_UnlockPath(psrcPath);
206 }
207 return TRUE;
208 }
209
210 BOOL PATH_RestorePath( DC *dst, DC *src )
211 {
212 DPRINT("PATH_RestorePath\n");
213
214 if (dst->dclevel.hPath == NULL)
215 {
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;
221
222 PATH_AssignGdiPath(pdstPath, psrcPath);
223
224 PATH_UnlockPath(pdstPath);
225 PATH_UnlockPath(psrcPath);
226 }
227 else
228 {
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);
234
235 PATH_UnlockPath(pdstPath);
236 PATH_UnlockPath(psrcPath);
237 }
238 return TRUE;
239 }
240
241 /* PATH_EmptyPath
242 *
243 * Removes all entries from the path and sets the path state to PATH_Null.
244 */
245 VOID
246 FASTCALL
247 PATH_EmptyPath(PPATH pPath)
248 {
249 ASSERT(pPath != NULL);
250
251 pPath->state = PATH_Null;
252 pPath->numEntriesUsed = 0;
253 }
254
255 /* PATH_AddEntry
256 *
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).
260 */
261 BOOL
262 FASTCALL
263 PATH_AddEntry(
264 PPATH pPath,
265 const POINT *pPoint,
266 BYTE flags)
267 {
268 ASSERT(pPath != NULL);
269
270 /* FIXME: If newStroke is true, perhaps we want to check that we're
271 * getting a PT_MOVETO
272 */
273 DPRINT("(%d,%d) - %d\n", pPoint->x, pPoint->y, flags);
274
275 /* Reserve enough memory for an extra path entry */
276 if (!PATH_ReserveEntries(pPath, pPath->numEntriesUsed + 1))
277 return FALSE;
278
279 /* Store information in path entry */
280 pPath->pPoints[pPath->numEntriesUsed] = *pPoint;
281 pPath->pFlags[pPath->numEntriesUsed] = flags;
282
283 /* Increment entry count */
284 pPath->numEntriesUsed++;
285
286 return TRUE;
287 }
288
289 /* PATH_ReserveEntries
290 *
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.
294 */
295 BOOL
296 FASTCALL
297 PATH_ReserveEntries(
298 PPATH pPath,
299 INT numEntries)
300 {
301 INT numEntriesToAllocate;
302 POINT *pPointsNew;
303 BYTE *pFlagsNew;
304
305 ASSERT(pPath != NULL);
306 ASSERT(numEntries >= 0);
307
308 /* Do we have to allocate more memory? */
309 if (numEntries > pPath->numEntriesAllocated)
310 {
311 /* Find number of entries to allocate. We let the size of the array
312 * grow exponentially, since that will guarantee linear time
313 * complexity. */
314 if (pPath->numEntriesAllocated)
315 {
316 numEntriesToAllocate = pPath->numEntriesAllocated;
317 while (numEntriesToAllocate < numEntries)
318 numEntriesToAllocate = numEntriesToAllocate * GROW_FACTOR_NUMER / GROW_FACTOR_DENOM;
319 }
320 else
321 numEntriesToAllocate = numEntries;
322
323 /* Allocate new arrays */
324 pPointsNew = (POINT *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(POINT), TAG_PATH);
325 if (!pPointsNew)
326 return FALSE;
327
328 pFlagsNew = (BYTE *)ExAllocatePoolWithTag(PagedPool, numEntriesToAllocate * sizeof(BYTE), TAG_PATH);
329 if (!pFlagsNew)
330 {
331 ExFreePoolWithTag(pPointsNew, TAG_PATH);
332 return FALSE;
333 }
334
335 /* Copy old arrays to new arrays and discard old arrays */
336 if (pPath->pPoints)
337 {
338 ASSERT(pPath->pFlags);
339
340 memcpy(pPointsNew, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
341 memcpy(pFlagsNew, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
342
343 ExFreePoolWithTag(pPath->pPoints, TAG_PATH);
344 ExFreePoolWithTag(pPath->pFlags, TAG_PATH);
345 }
346
347 pPath->pPoints = pPointsNew;
348 pPath->pFlags = pFlagsNew;
349 pPath->numEntriesAllocated = numEntriesToAllocate;
350 }
351
352 return TRUE;
353 }
354
355 /* PATH_ScaleNormalizedPoint
356 *
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].
361 */
362 VOID
363 FASTCALL
364 PATH_ScaleNormalizedPoint(
365 FLOAT_POINT corners[],
366 double x,
367 double y,
368 POINT *pPoint)
369 {
370 ASSERT(corners);
371 ASSERT(pPoint);
372
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));
375 }
376
377 /* PATH_NormalizePoint
378 *
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.
381 */
382 VOID
383 FASTCALL
384 PATH_NormalizePoint(
385 FLOAT_POINT corners[],
386 const FLOAT_POINT *pPoint,
387 double *pX,
388 double *pY)
389 {
390 ASSERT(corners);
391 ASSERT(pPoint);
392 ASSERT(pX);
393 ASSERT(pY);
394
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;
397 }
398
399 /* PATH_CheckCorners
400 *
401 * Helper function for PATH_RoundRect() and PATH_Rectangle()
402 */
403 BOOL
404 PATH_CheckCorners(
405 DC *dc,
406 POINT corners[],
407 INT x1,
408 INT y1,
409 INT x2,
410 INT y2)
411 {
412 INT temp;
413 PDC_ATTR pdcattr = dc->pdcattr;
414
415 /* Convert points to device coordinates */
416 corners[0].x = x1;
417 corners[0].y = y1;
418 corners[1].x = x2;
419 corners[1].y = y2;
420 IntLPtoDP(dc, corners, 2);
421
422 /* Make sure first corner is top left and second corner is bottom right */
423 if (corners[0].x > corners[1].x)
424 {
425 temp = corners[0].x;
426 corners[0].x = corners[1].x;
427 corners[1].x = temp;
428 }
429
430 if (corners[0].y > corners[1].y)
431 {
432 temp = corners[0].y;
433 corners[0].y = corners[1].y;
434 corners[1].y = temp;
435 }
436
437 /* In GM_COMPATIBLE, don't include bottom and right edges */
438 if (pdcattr->iGraphicsMode == GM_COMPATIBLE)
439 {
440 if (corners[0].x == corners[1].x) return FALSE;
441 if (corners[0].y == corners[1].y) return FALSE;
442 corners[1].x--;
443 corners[1].y--;
444 }
445 return TRUE;
446 }
447
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 )
452 {
453 BYTE *ret;
454
455 if (!PATH_ReserveEntries( path, path->numEntriesUsed + count )) return NULL;
456
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;
462 return ret;
463 }
464
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 )
468 {
469 BYTE *ret;
470
471 if (!PATH_ReserveEntries( path, path->numEntriesUsed + count )) return NULL;
472
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;
477 return ret;
478 }
479
480 /* reverse the order of an array of points */
481 static void reverse_points( POINT *points, UINT count )
482 {
483 UINT i;
484 for (i = 0; i < count / 2; i++)
485 {
486 POINT pt = points[i];
487 points[i] = points[count - i - 1];
488 points[count - i - 1] = pt;
489 }
490 }
491
492 /* start a new path stroke if necessary */
493 static BOOL start_new_stroke( PPATH path )
494 {
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)
499 return TRUE;
500
501 path->newStroke = FALSE;
502 return add_points( path, &path->pos, 1, PT_MOVETO ) != NULL;
503 }
504
505 /* set current position to the last point that was added to the path */
506 static void update_current_pos( PPATH path )
507 {
508 assert( path->numEntriesUsed );
509 path->pos = path->pPoints[path->numEntriesUsed - 1];
510 }
511
512 /* close the current figure */
513 static void close_figure( PPATH path )
514 {
515 assert( path->numEntriesUsed );
516 path->pFlags[path->numEntriesUsed - 1] |= PT_CLOSEFIGURE;
517 }
518
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 )
522 {
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 );
526
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);
529
530 return TRUE;
531 }
532
533 /* PATH_MoveTo
534 *
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
537 * FALSE.
538 */
539 BOOL
540 FASTCALL
541 PATH_MoveTo(
542 PDC dc,
543 PPATH pPath)
544 {
545 if (!pPath) return FALSE;
546
547 // GDI32 : Signal from user space of a change in position.
548 if (dc->pdcattr->ulDirty_ & DIRTY_STYLESTATE)
549 {
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 );
555 return TRUE;
556 }
557
558 return FALSE;
559 }
560
561 /* PATH_LineTo
562 *
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.
567 */
568 BOOL
569 FASTCALL
570 PATH_LineTo(
571 PDC dc,
572 INT x,
573 INT y)
574 {
575 BOOL Ret;
576 PPATH pPath;
577 POINT point, pointCurPos;
578
579 pPath = PATH_LockPath(dc->dclevel.hPath);
580 if (!pPath) return FALSE;
581
582 point.x = x;
583 point.y = y;
584
585 // Coalesce a MoveTo point.
586 if ( !PATH_MoveTo(dc, pPath) )
587 {
588 /* Add a PT_MOVETO if necessary */
589 if (pPath->newStroke)
590 {
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))
596 {
597 PATH_UnlockPath(pPath);
598 return FALSE;
599 }
600 }
601 }
602 Ret = add_log_points_new_stroke( dc, pPath, &point, 1, PT_LINETO );
603 PATH_UnlockPath(pPath);
604 return Ret;
605 }
606
607 /* PATH_Rectangle
608 *
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.
611 */
612 BOOL
613 FASTCALL
614 PATH_Rectangle(
615 PDC dc,
616 INT x1,
617 INT y1,
618 INT x2,
619 INT y2)
620 {
621 PPATH pPath;
622 POINT corners[2], points[4];
623 BYTE *type;
624
625 pPath = PATH_LockPath(dc->dclevel.hPath);
626 if (!pPath) return FALSE;
627
628 if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
629 {
630 PATH_UnlockPath(pPath);
631 return TRUE;
632 }
633
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];
640
641 if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 4 );
642
643 if (!(type = add_points( pPath, points, 4, PT_LINETO )))
644 {
645 PATH_UnlockPath(pPath);
646 return FALSE;
647 }
648 type[0] = PT_MOVETO;
649
650 /* Close the rectangle figure */
651 IntGdiCloseFigure(pPath) ;
652 PATH_UnlockPath(pPath);
653 return TRUE;
654 }
655
656 /* PATH_RoundRect
657 *
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.
660 *
661 */
662 BOOL
663 FASTCALL
664 PATH_RoundRect(
665 DC *dc,
666 INT x1,
667 INT y1,
668 INT x2,
669 INT y2,
670 INT ell_width,
671 INT ell_height)
672 {
673 const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
674 PPATH pPath;
675 POINT corners[2], ellipse[2], points[16];
676 BYTE *type;
677 double width, height;
678
679 if (!ell_width || !ell_height) return PATH_Rectangle( dc, x1, y1, x2, y2 );
680
681 pPath = PATH_LockPath(dc->dclevel.hPath);
682 if (!pPath) return FALSE;
683
684 if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
685 {
686 PATH_UnlockPath(pPath);
687 return TRUE;
688 }
689
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;
698
699 /* starting point */
700 points[0].x = corners[1].x;
701 points[0].y = corners[0].y + GDI_ROUND( height );
702 /* first curve */
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;
712 /* second curve */
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 );
719 /* vertical line */
720 points[8].x = corners[0].x;
721 points[8].y = corners[1].y - GDI_ROUND( height );
722 /* third curve */
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;
732 /* fourth curve */
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 );
739
740 if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 16 );
741 if (!(type = add_points( pPath, points, 16, PT_BEZIERTO )))
742 {
743 PATH_UnlockPath(pPath);
744 return FALSE;
745 }
746 type[0] = PT_MOVETO;
747 type[4] = type[8] = type[12] = PT_LINETO;
748
749 IntGdiCloseFigure(pPath);
750 PATH_UnlockPath(pPath);
751 return TRUE;
752 }
753
754 /* PATH_Ellipse
755 *
756 */
757 BOOL
758 FASTCALL
759 PATH_Ellipse(
760 PDC dc,
761 INT x1,
762 INT y1,
763 INT x2,
764 INT y2)
765 {
766 const double factor = 0.55428475; /* 4 / 3 * (sqrt(2) - 1) */
767 PPATH pPath;
768 POINT corners[2], points[13];
769 BYTE *type;
770 double width, height;
771
772 pPath = PATH_LockPath(dc->dclevel.hPath);
773 if (!pPath) return FALSE;
774
775 if (!PATH_CheckCorners(dc, corners, x1, y1, x2, y2))
776 {
777 PATH_UnlockPath(pPath);
778 return TRUE;
779 }
780
781 width = (corners[1].x - corners[0].x) / 2.0;
782 height = (corners[1].y - corners[0].y) / 2.0;
783
784 /* starting point */
785 points[0].x = corners[1].x;
786 points[0].y = corners[0].y + GDI_ROUND( height );
787 /* first curve */
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;
794 /* second curve */
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 );
801 /* third curve */
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;
808 /* fourth curve */
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 );
815
816 if (dc->dclevel.flPath & DCPATH_CLOCKWISE) reverse_points( points, 13 );
817 if (!(type = add_points( pPath, points, 13, PT_BEZIERTO )))
818 {
819 DPRINT1("PATH_Ellipse No add\n");
820 PATH_UnlockPath(pPath);
821 return FALSE;
822 }
823 type[0] = PT_MOVETO;
824
825 IntGdiCloseFigure(pPath);
826 PATH_UnlockPath(pPath);
827 return TRUE;
828 }
829
830 /* PATH_DoArcPart
831 *
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.
838 */
839 BOOL
840 FASTCALL
841 PATH_DoArcPart(
842 PPATH pPath,
843 FLOAT_POINT corners[],
844 double angleStart,
845 double angleEnd,
846 BYTE startEntryType)
847 {
848 double halfAngle, a;
849 double xNorm[4], yNorm[4];
850 POINT points[4];
851 BYTE *type;
852 int i, start;
853
854 ASSERT(fabs(angleEnd - angleStart) <= M_PI_2);
855
856 /* FIXME: Is there an easier way of computing this? */
857
858 /* Compute control points */
859 halfAngle = (angleEnd - angleStart) / 2.0;
860 if (fabs(halfAngle) > 1e-8)
861 {
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];
871 }
872 else
873 for (i = 0; i < 4; i++)
874 {
875 xNorm[i] = cos(angleStart);
876 yNorm[i] = sin(angleStart);
877 }
878
879 /* Add starting point to path if desired */
880 start = !startEntryType;
881
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;
886
887 return TRUE;
888 }
889
890 /* PATH_Arc
891 *
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,
898 * else FALSE.
899 */
900 BOOL
901 FASTCALL
902 PATH_Arc(
903 PDC dc,
904 INT x1,
905 INT y1,
906 INT x2,
907 INT y2,
908 INT xStart,
909 INT yStart,
910 INT xEnd,
911 INT yEnd,
912 INT direction,
913 INT lines)
914 {
915 double angleStart, angleEnd, angleStartQuadrant, angleEndQuadrant = 0.0;
916 /* Initialize angleEndQuadrant to silence gcc's warning */
917 double x, y;
918 FLOAT_POINT corners[2], pointStart, pointEnd;
919 POINT centre, pointCurPos;
920 BOOL start, end, Ret = TRUE;
921 INT temp;
922 BOOL clockwise;
923 PPATH pPath;
924
925 /* FIXME: This function should check for all possible error returns */
926 /* FIXME: Do we have to respect newStroke? */
927
928 ASSERT(dc);
929
930 pPath = PATH_LockPath(dc->dclevel.hPath);
931 if (!pPath) return FALSE;
932
933 if (direction)
934 clockwise = ((direction == AD_CLOCKWISE) !=0 );
935 else
936 clockwise = ((dc->dclevel.flPath & DCPATH_CLOCKWISE) != 0);
937
938 /* Check for zero height / width */
939 /* FIXME: Only in GM_COMPATIBLE? */
940 if (x1 == x2 || y1 == y2)
941 {
942 Ret = TRUE;
943 goto ArcExit;
944 }
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);
958
959 /* Make sure first corner is top left and second corner is bottom right */
960 if (corners[0].x > corners[1].x)
961 {
962 temp = corners[0].x;
963 corners[0].x = corners[1].x;
964 corners[1].x = temp;
965 }
966 if (corners[0].y > corners[1].y)
967 {
968 temp = corners[0].y;
969 corners[0].y = corners[1].y;
970 corners[1].y = temp;
971 }
972
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);
978
979 /* Make sure the end angle is "on the right side" of the start angle */
980 if (clockwise)
981 {
982 if (angleEnd <= angleStart)
983 {
984 angleEnd += 2 * M_PI;
985 ASSERT(angleEnd >= angleStart);
986 }
987 }
988 else
989 {
990 if (angleEnd >= angleStart)
991 {
992 angleEnd -= 2 * M_PI;
993 ASSERT(angleEnd <= angleStart);
994 }
995 }
996
997 /* In GM_COMPATIBLE, don't include bottom and right edges */
998 if (dc->pdcattr->iGraphicsMode == GM_COMPATIBLE)
999 {
1000 corners[1].x--;
1001 corners[1].y--;
1002 }
1003
1004 /* arcto: Add a PT_MOVETO only if this is the first entry in a stroke */
1005 if (lines == GdiTypeArcTo && pPath->newStroke) // -1
1006 {
1007 pPath->newStroke = FALSE;
1008 IntGetCurrentPositionEx(dc, &pointCurPos);
1009 CoordLPtoDP(dc, &pointCurPos);
1010 if (!PATH_AddEntry(pPath, &pointCurPos, PT_MOVETO))
1011 {
1012 Ret = FALSE;
1013 goto ArcExit;
1014 }
1015 }
1016
1017 /* Add the arc to the path with one Bezier spline per quadrant that the
1018 * arc spans */
1019 start = TRUE;
1020 end = FALSE;
1021 do
1022 {
1023 /* Determine the start and end angles for this quadrant */
1024 if (start)
1025 {
1026 angleStartQuadrant = angleStart;
1027 if (clockwise)
1028 angleEndQuadrant = (floor(angleStart / M_PI_2) + 1.0) * M_PI_2;
1029 else
1030 angleEndQuadrant = (ceil(angleStart / M_PI_2) - 1.0) * M_PI_2;
1031 }
1032 else
1033 {
1034 angleStartQuadrant = angleEndQuadrant;
1035 if (clockwise)
1036 angleEndQuadrant += M_PI_2;
1037 else
1038 angleEndQuadrant -= M_PI_2;
1039 }
1040
1041 /* Have we reached the last part of the arc? */
1042 if ((clockwise && angleEnd < angleEndQuadrant) ||
1043 (!clockwise && angleEnd > angleEndQuadrant))
1044 {
1045 /* Adjust the end angle for this quadrant */
1046 angleEndQuadrant = angleEnd;
1047 end = TRUE;
1048 }
1049
1050 /* Add the Bezier spline to the path */
1051 PATH_DoArcPart(pPath,
1052 corners,
1053 angleStartQuadrant,
1054 angleEndQuadrant,
1055 start ? (lines == GdiTypeArcTo ? PT_LINETO : PT_MOVETO) : FALSE); // -1
1056 start = FALSE;
1057 }
1058 while (!end);
1059
1060 if (lines == GdiTypeArcTo)
1061 {
1062 update_current_pos( pPath );
1063 }
1064 else /* chord: close figure. pie: add line and close figure */
1065 if (lines == GdiTypeChord) // 1
1066 {
1067 IntGdiCloseFigure(pPath);
1068 }
1069 else if (lines == GdiTypePie) // 2
1070 {
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, &centre, PT_LINETO | PT_CLOSEFIGURE))
1074 Ret = FALSE;
1075 }
1076 ArcExit:
1077 PATH_UnlockPath(pPath);
1078 return Ret;
1079 }
1080
1081 BOOL
1082 FASTCALL
1083 PATH_PolyBezierTo(
1084 PDC dc,
1085 const POINT *pts,
1086 DWORD cbPoints)
1087 {
1088 PPATH pPath;
1089 BOOL ret;
1090
1091 ASSERT(dc);
1092 ASSERT(pts);
1093 ASSERT(cbPoints);
1094
1095 pPath = PATH_LockPath(dc->dclevel.hPath);
1096 if (!pPath) return FALSE;
1097
1098 ret = add_log_points_new_stroke( dc, pPath, pts, cbPoints, PT_BEZIERTO );
1099
1100 PATH_UnlockPath(pPath);
1101 return ret;
1102 }
1103
1104 BOOL
1105 FASTCALL
1106 PATH_PolyBezier(
1107 PDC dc,
1108 const POINT *pts,
1109 DWORD cbPoints)
1110 {
1111 PPATH pPath;
1112 BYTE *type;
1113
1114 ASSERT(dc);
1115 ASSERT(pts);
1116 ASSERT(cbPoints);
1117
1118 pPath = PATH_LockPath(dc->dclevel.hPath);
1119 if (!pPath) return FALSE;
1120
1121 type = add_log_points( dc, pPath, pts, cbPoints, PT_BEZIERTO );
1122 if (!type) return FALSE;
1123
1124 type[0] = PT_MOVETO;
1125
1126 PATH_UnlockPath(pPath);
1127 return TRUE;
1128 }
1129
1130 BOOL
1131 FASTCALL
1132 PATH_PolyDraw(
1133 PDC dc,
1134 const POINT *pts,
1135 const BYTE *types,
1136 DWORD cbPoints)
1137 {
1138 PPATH pPath;
1139 POINT orig_pos, cur_pos;
1140 ULONG i, lastmove = 0;
1141
1142 pPath = PATH_LockPath(dc->dclevel.hPath);
1143 if (!pPath) return FALSE;
1144
1145 if (pPath->state != PATH_Open)
1146 {
1147 PATH_UnlockPath(pPath);
1148 return FALSE;
1149 }
1150
1151 for (i = 0; i < pPath->numEntriesUsed; i++) if (pPath->pFlags[i] == PT_MOVETO) lastmove = i;
1152 orig_pos = pPath->pos;
1153
1154 IntGetCurrentPositionEx(dc, &cur_pos);
1155
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);
1158
1159
1160 for(i = 0; i < cbPoints; i++)
1161 {
1162 switch (types[i])
1163 {
1164 case PT_MOVETO:
1165 pPath->newStroke = TRUE;
1166 pPath->pos = pts[i];
1167 IntLPtoDP( dc, &pPath->pos, 1);
1168 lastmove = pPath->numEntriesUsed;
1169 break;
1170 case PT_LINETO:
1171 case PT_LINETO | PT_CLOSEFIGURE:
1172 if (!add_log_points_new_stroke( dc, pPath, &pts[i], 1, PT_LINETO ))
1173 {
1174 PATH_UnlockPath(pPath);
1175 return FALSE;
1176 }
1177 break;
1178 case PT_BEZIERTO:
1179 if ((i + 2 < cbPoints) && (types[i + 1] == PT_BEZIERTO) &&
1180 (types[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)
1181 {
1182 if (!add_log_points_new_stroke( dc, pPath, &pts[i], 3, PT_BEZIERTO ))
1183 {
1184 PATH_UnlockPath(pPath);
1185 return FALSE;
1186 }
1187 i += 2;
1188 break;
1189 }
1190 /* fall through */
1191 default:
1192 /* restore original position */
1193 pPath->pos = orig_pos;
1194
1195 DPRINT("PPD Bad : pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
1196
1197 IntGdiMoveToEx(dc, cur_pos.x, cur_pos.y, NULL);
1198
1199 PATH_UnlockPath(pPath);
1200 return FALSE;
1201 }
1202
1203 if (types[i] & PT_CLOSEFIGURE)
1204 {
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);
1208 }
1209 }
1210 PATH_UnlockPath(pPath);
1211 return TRUE;
1212 }
1213
1214 BOOL
1215 FASTCALL
1216 PATH_PolylineTo(
1217 PDC dc,
1218 const POINT *pts,
1219 DWORD cbPoints)
1220 {
1221 PPATH pPath;
1222 BOOL ret;
1223
1224 ASSERT(dc);
1225 ASSERT(pts);
1226 ASSERT(cbPoints);
1227
1228 if (cbPoints < 1) return FALSE;
1229
1230 pPath = PATH_LockPath(dc->dclevel.hPath);
1231 if (!pPath) return FALSE;
1232
1233 ret = add_log_points_new_stroke( dc, pPath, pts, cbPoints, PT_LINETO );
1234 PATH_UnlockPath(pPath);
1235 return ret;
1236 }
1237
1238 BOOL
1239 FASTCALL
1240 PATH_PolyPolygon(
1241 PDC dc,
1242 const POINT* pts,
1243 const INT* counts,
1244 UINT polygons)
1245 {
1246 UINT poly, count;
1247 BYTE *type;
1248 PPATH pPath;
1249
1250 ASSERT(dc);
1251 ASSERT(pts);
1252 ASSERT(counts);
1253 ASSERT(polygons);
1254
1255 if (!polygons) return FALSE;
1256
1257 pPath = PATH_LockPath(dc->dclevel.hPath);
1258 if (!pPath) return FALSE;
1259
1260
1261 for (poly = count = 0; poly < polygons; poly++)
1262 {
1263 if (counts[poly] < 2)
1264 {
1265 PATH_UnlockPath(pPath);
1266 return FALSE;
1267 }
1268 count += counts[poly];
1269 }
1270
1271 type = add_log_points( dc, pPath, pts, count, PT_LINETO );
1272 if (!type)
1273 {
1274 PATH_UnlockPath(pPath);
1275 return FALSE;
1276 }
1277
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++])
1280 {
1281 type[0] = PT_MOVETO;
1282 type[counts[poly] - 1] = PT_LINETO | PT_CLOSEFIGURE;
1283 }
1284 PATH_UnlockPath(pPath);
1285 return TRUE;
1286 }
1287
1288 BOOL
1289 FASTCALL
1290 PATH_PolyPolyline(
1291 PDC dc,
1292 const POINT* pts,
1293 const DWORD* counts,
1294 DWORD polylines)
1295 {
1296 POINT pt;
1297 ULONG poly, point, i;
1298 PPATH pPath;
1299
1300 ASSERT(dc);
1301 ASSERT(pts);
1302 ASSERT(counts);
1303 ASSERT(polylines);
1304
1305 pPath = PATH_LockPath(dc->dclevel.hPath);
1306 if (!pPath)
1307 {
1308 return FALSE;
1309 }
1310
1311 for (i = 0, poly = 0; poly < polylines; poly++)
1312 {
1313 for (point = 0; point < counts[poly]; point++, i++)
1314 {
1315 pt = pts[i];
1316 CoordLPtoDP(dc, &pt);
1317 PATH_AddEntry(pPath, &pt, (point == 0) ? PT_MOVETO : PT_LINETO);
1318 }
1319 }
1320 DPRINT("PATH_PolyPolyline end count %d\n",pPath->numEntriesUsed);
1321 PATH_UnlockPath(pPath);
1322 return TRUE;
1323 }
1324
1325 /* PATH_AddFlatBezier
1326 *
1327 */
1328 BOOL
1329 FASTCALL
1330 PATH_AddFlatBezier(
1331 PPATH pPath,
1332 POINT *pt,
1333 BOOL closed)
1334 {
1335 POINT *pts;
1336 BOOL ret = FALSE;
1337 INT no, i;
1338
1339 pts = GDI_Bezier(pt, 4, &no);
1340 if (!pts) return FALSE;
1341
1342 for (i = 1; i < no; i++)
1343 {
1344 if (!(ret = PATH_AddEntry(pPath, &pts[i], (i == no - 1 && closed) ? PT_LINETO | PT_CLOSEFIGURE : PT_LINETO)))
1345 break;
1346 }
1347
1348 ExFreePoolWithTag(pts, TAG_BEZIER);
1349 return ret;
1350 }
1351
1352 /* PATH_FlattenPath
1353 *
1354 * Replaces Beziers with line segments
1355 *
1356 */
1357 PPATH
1358 FASTCALL
1359 PATH_FlattenPath(PPATH pPath)
1360 {
1361 PPATH newPath;
1362 INT srcpt;
1363 DPRINT("PATH_FlattenPath\n");
1364 if (!(newPath = PATH_CreatePath(pPath->numEntriesUsed))) return NULL;
1365
1366 for (srcpt = 0; srcpt < pPath->numEntriesUsed; srcpt++)
1367 {
1368 switch(pPath->pFlags[srcpt] & ~PT_CLOSEFIGURE)
1369 {
1370 case PT_MOVETO:
1371 case PT_LINETO:
1372 if (!PATH_AddEntry(newPath, &pPath->pPoints[srcpt], pPath->pFlags[srcpt]))
1373 {
1374 PATH_UnlockPath(newPath);
1375 PATH_Delete(newPath->BaseObject.hHmgr);
1376 return NULL;
1377 }
1378 break;
1379 case PT_BEZIERTO:
1380 if(!PATH_AddFlatBezier(newPath, &pPath->pPoints[srcpt - 1], pPath->pFlags[srcpt + 2] & PT_CLOSEFIGURE))
1381 {
1382 PATH_UnlockPath(newPath);
1383 PATH_Delete(newPath->BaseObject.hHmgr);
1384 return NULL;
1385 }
1386 srcpt += 2;
1387 break;
1388 }
1389 }
1390 DPRINT("PATH_FlattenPath good\n");
1391 newPath->state = pPath->state;
1392 return newPath;
1393 }
1394
1395 /* PATH_PathToRegion
1396 *
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.
1400 */
1401 BOOL
1402 FASTCALL
1403 PATH_PathToRegion(
1404 PPATH pPath,
1405 INT Mode,
1406 PREGION Rgn)
1407 {
1408 int i, pos, polygons;
1409 PULONG counts;
1410 int Ret;
1411
1412 if (!pPath->numEntriesUsed) return FALSE;
1413
1414 counts = ExAllocatePoolWithTag(PagedPool, (pPath->numEntriesUsed / 2) * sizeof(counts), TAG_PATH);
1415 if (!counts)
1416 {
1417 DPRINT1("Failed to allocate %lu strokes\n", (pPath->numEntriesUsed / 2) * sizeof(*counts));
1418 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1419 return FALSE;
1420 }
1421
1422 pos = polygons = 0;
1423 ASSERT( pPath->pFlags[0] == PT_MOVETO );
1424 for (i = 1; i < pPath->numEntriesUsed; i++)
1425 {
1426 if (pPath->pFlags[i] != PT_MOVETO) continue;
1427 counts[polygons++] = i - pos;
1428 pos = i;
1429 }
1430 if (i > pos + 1) counts[polygons++] = i - pos;
1431
1432 ASSERT( polygons <= pPath->numEntriesUsed / 2 );
1433
1434 /* Fill the region with the strokes */
1435 Ret = REGION_SetPolyPolygonRgn(Rgn,
1436 pPath->pPoints,
1437 counts,
1438 polygons,
1439 Mode);
1440 if (!Ret)
1441 {
1442 DPRINT1("REGION_SetPolyPolygonRgn failed\n");
1443 }
1444
1445 ExFreePoolWithTag(counts, TAG_PATH);
1446
1447 /* Success! */
1448 return Ret;
1449 }
1450
1451 /* PATH_FillPath
1452 *
1453 * You can play with this as long as you like, but if you break Area.exe the purge will Begain on Path!!!
1454 *
1455 */
1456 BOOL
1457 FASTCALL
1458 PATH_FillPath(
1459 PDC dc,
1460 PPATH pPath)
1461 {
1462 return PATH_FillPathEx(dc, pPath, NULL);
1463 }
1464
1465 BOOL
1466 FASTCALL
1467 PATH_FillPathEx(
1468 PDC dc,
1469 PPATH pPath,
1470 PBRUSH pbrFill)
1471 {
1472 INT mapMode, graphicsMode;
1473 SIZE ptViewportExt, ptWindowExt;
1474 POINTL ptViewportOrg, ptWindowOrg;
1475 XFORML xform;
1476 PREGION Rgn;
1477 PDC_ATTR pdcattr = dc->pdcattr;
1478
1479 /* Allocate a temporary region */
1480 Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
1481 if (!Rgn)
1482 {
1483 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1484 return FALSE;
1485 }
1486
1487 if (!PATH_PathToRegion(pPath, pdcattr->jFillMode, Rgn))
1488 {
1489 DPRINT("PFP : Fail P2R\n");
1490 /* EngSetLastError ? */
1491 REGION_Delete(Rgn);
1492 return FALSE;
1493 }
1494
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.
1501 */
1502
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;
1509
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.
1515 */
1516 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1517
1518 /* Set MM_TEXT */
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;
1524
1525 graphicsMode = pdcattr->iGraphicsMode;
1526 pdcattr->iGraphicsMode = GM_ADVANCED;
1527 GreModifyWorldTransform(dc, &xform, MWT_IDENTITY);
1528 pdcattr->iGraphicsMode = graphicsMode;
1529
1530 /* Paint the region */
1531 IntGdiFillRgn(dc, Rgn, pbrFill);
1532 REGION_Delete(Rgn);
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;
1539
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;
1545 return TRUE;
1546 }
1547
1548 BOOL
1549 FASTCALL
1550 PATH_StrokePath(
1551 DC *dc,
1552 PPATH pPath)
1553 {
1554 BOOL ret = FALSE;
1555 INT i = 0;
1556 INT nLinePts, nAlloc;
1557 POINT *pLinePts = NULL;
1558 POINT ptViewportOrg, ptWindowOrg;
1559 SIZE szViewportExt, szWindowExt;
1560 DWORD mapMode, graphicsMode;
1561 XFORM xform;
1562 PDC_ATTR pdcattr = dc->pdcattr;
1563
1564 DPRINT("Enter %s\n", __FUNCTION__);
1565
1566 /* Save the mapping mode info */
1567 mapMode = pdcattr->iMapMode;
1568
1569 szViewportExt = *DC_pszlViewportExt(dc);
1570 ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1571 szWindowExt = dc->pdcattr->szlWindowExt;
1572 ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1573
1574 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1575
1576 /* Set MM_TEXT */
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;
1586
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);
1592 if (!pLinePts)
1593 {
1594 DPRINT1("Can't allocate pool!\n");
1595 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1596 goto end;
1597 }
1598 nLinePts = 0;
1599
1600 for (i = 0; i < pPath->numEntriesUsed; i++)
1601 {
1602 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1603 && (pPath->pFlags[i] != PT_MOVETO))
1604 {
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]);
1608 goto end;
1609 }
1610
1611 switch(pPath->pFlags[i])
1612 {
1613 case PT_MOVETO:
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);
1617 nLinePts = 0;
1618 pLinePts[nLinePts++] = pPath->pPoints[i];
1619 break;
1620 case PT_LINETO:
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];
1625 break;
1626 case PT_BEZIERTO:
1627 DPRINT("Got PT_BEZIERTO\n");
1628 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1629 (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1630 {
1631 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1632 ret = FALSE;
1633 goto end;
1634 }
1635 else
1636 {
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)
1644 {
1645 // Reallocate memory
1646
1647 POINT *Realloc = NULL;
1648 nAlloc = nMinAlloc * 2;
1649
1650 Realloc = ExAllocatePoolWithTag(PagedPool,
1651 nAlloc * sizeof(POINT),
1652 TAG_PATH);
1653
1654 if (!Realloc)
1655 {
1656 DPRINT1("Can't allocate pool!\n");
1657 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1658 goto end;
1659 }
1660
1661 memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1662 ExFreePoolWithTag(pLinePts, TAG_PATH);
1663 pLinePts = Realloc;
1664 }
1665 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1666 nLinePts += nBzrPts - 1;
1667 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1668 i += 2;
1669 }
1670 break;
1671 default:
1672 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1673 goto end;
1674 }
1675
1676 if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1677 {
1678 pLinePts[nLinePts++] = pLinePts[0];
1679 }
1680 }
1681 if (nLinePts >= 2)
1682 IntGdiPolyline(dc, pLinePts, nLinePts);
1683
1684 ret = TRUE;
1685
1686 end:
1687 if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1688
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;
1695
1696 pdcattr->szlViewportExt.cx = szViewportExt.cx;
1697 pdcattr->szlViewportExt.cy = szViewportExt.cy;
1698 pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1699 pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1700
1701 /* Restore the world transform */
1702 XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1703
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
1708 mode.
1709 */
1710 if (i > 0)
1711 {
1712 POINT pt;
1713 IntGetCurrentPositionEx(dc, &pt);
1714 IntDPtoLP(dc, &pt, 1);
1715 IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1716 }
1717 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1718 return ret;
1719 }
1720
1721 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1722
1723 PPATH FASTCALL
1724 IntGdiWidenPath(PPATH pPath, UINT penWidth, UINT penStyle, FLOAT eMiterLimit)
1725 {
1726 INT i, j, numStrokes, numOldStrokes, penWidthIn, penWidthOut;
1727 PPATH flat_path, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1728 BYTE *type;
1729 DWORD joint, endcap;
1730
1731 endcap = (PS_ENDCAP_MASK & penStyle);
1732 joint = (PS_JOIN_MASK & penStyle);
1733
1734 if (!(flat_path = PATH_FlattenPath(pPath)))
1735 {
1736 DPRINT1("PATH_FlattenPath\n");
1737 return NULL;
1738 }
1739
1740 penWidthIn = penWidth / 2;
1741 penWidthOut = penWidth / 2;
1742 if (penWidthIn + penWidthOut < penWidth)
1743 penWidthOut++;
1744
1745 numStrokes = 0;
1746
1747 for (i = 0, j = 0; i < flat_path->numEntriesUsed; i++, j++)
1748 {
1749 POINT point;
1750 if ((i == 0 || (flat_path->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1751 (flat_path->pFlags[i] != PT_MOVETO))
1752 {
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]);
1756 if (pStrokes)
1757 ExFreePoolWithTag(pStrokes, TAG_PATH);
1758 PATH_UnlockPath(flat_path);
1759 PATH_Delete(flat_path->BaseObject.hHmgr);
1760 return NULL;
1761 }
1762 switch(flat_path->pFlags[i])
1763 {
1764 case PT_MOVETO:
1765 if (numStrokes > 0)
1766 {
1767 pStrokes[numStrokes - 1]->state = PATH_Closed;
1768 }
1769 numOldStrokes = numStrokes;
1770 numStrokes++;
1771 j = 0;
1772 if (numStrokes == 1)
1773 pStrokes = ExAllocatePoolWithTag(PagedPool, sizeof(*pStrokes), TAG_PATH);
1774 else
1775 {
1776 pOldStrokes = pStrokes; // Save old pointer.
1777 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(*pStrokes), TAG_PATH);
1778 if (!pStrokes)
1779 {
1780 PATH_UnlockPath(flat_path);
1781 PATH_Delete(flat_path->BaseObject.hHmgr);
1782 return NULL;
1783 }
1784 RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1785 ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1786 }
1787 if (!pStrokes)
1788 {
1789 PATH_UnlockPath(flat_path);
1790 PATH_Delete(flat_path->BaseObject.hHmgr);
1791 return NULL;
1792 }
1793 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1794 if (!pStrokes[numStrokes - 1])
1795 {
1796 ASSERT(FALSE); // FIXME
1797 }
1798 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1799 pStrokes[numStrokes - 1]->state = PATH_Open;
1800 case PT_LINETO:
1801 case (PT_LINETO | PT_CLOSEFIGURE):
1802 point.x = flat_path->pPoints[i].x;
1803 point.y = flat_path->pPoints[i].y;
1804 PATH_AddEntry(pStrokes[numStrokes - 1], &point, flat_path->pFlags[i]);
1805 break;
1806 case PT_BEZIERTO:
1807 /* Should never happen because of the FlattenPath call */
1808 DPRINT1("Should never happen\n");
1809 break;
1810 default:
1811 DPRINT1("Got path flag %c\n", flat_path->pFlags[i]);
1812 if (pStrokes)
1813 ExFreePoolWithTag(pStrokes, TAG_PATH);
1814 PATH_UnlockPath(flat_path);
1815 PATH_Delete(flat_path->BaseObject.hHmgr);
1816 return NULL;
1817 }
1818 }
1819
1820 pNewPath = PATH_CreatePath( flat_path->numEntriesUsed );
1821
1822 for (i = 0; i < numStrokes; i++)
1823 {
1824 pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1825 PATH_InitGdiPath(pUpPath);
1826 pUpPath->state = PATH_Open;
1827 pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1828 PATH_InitGdiPath(pDownPath);
1829 pDownPath->state = PATH_Open;
1830
1831 for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1832 {
1833 /* Beginning or end of the path if not closed */
1834 if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1835 {
1836 /* Compute segment angle */
1837 double xo, yo, xa, ya, theta;
1838 POINT pt;
1839 FLOAT_POINT corners[2];
1840 if (j == 0)
1841 {
1842 xo = pStrokes[i]->pPoints[j].x;
1843 yo = pStrokes[i]->pPoints[j].y;
1844 xa = pStrokes[i]->pPoints[1].x;
1845 ya = pStrokes[i]->pPoints[1].y;
1846 }
1847 else
1848 {
1849 xa = pStrokes[i]->pPoints[j - 1].x;
1850 ya = pStrokes[i]->pPoints[j - 1].y;
1851 xo = pStrokes[i]->pPoints[j].x;
1852 yo = pStrokes[i]->pPoints[j].y;
1853 }
1854 theta = atan2(ya - yo, xa - xo);
1855 switch(endcap)
1856 {
1857 case PS_ENDCAP_SQUARE :
1858 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1859 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1860 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1861 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1862 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1863 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1864 break;
1865 case PS_ENDCAP_FLAT :
1866 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1867 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1868 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1869 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1870 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1871 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1872 break;
1873 case PS_ENDCAP_ROUND :
1874 default :
1875 corners[0].x = xo - penWidthIn;
1876 corners[0].y = yo - penWidthIn;
1877 corners[1].x = xo + penWidthOut;
1878 corners[1].y = yo + penWidthOut;
1879 PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1880 PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1881 PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1882 PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1883 break;
1884 }
1885 }
1886 /* Corpse of the path */
1887 else
1888 {
1889 /* Compute angle */
1890 INT previous, next;
1891 double xa, ya, xb, yb, xo, yo;
1892 double alpha, theta, miterWidth;
1893 DWORD _joint = joint;
1894 POINT pt;
1895 PPATH pInsidePath, pOutsidePath;
1896 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1897 {
1898 previous = j - 1;
1899 next = j + 1;
1900 }
1901 else if (j == 0)
1902 {
1903 previous = pStrokes[i]->numEntriesUsed - 1;
1904 next = j + 1;
1905 }
1906 else
1907 {
1908 previous = j - 1;
1909 next = 0;
1910 }
1911 xo = pStrokes[i]->pPoints[j].x;
1912 yo = pStrokes[i]->pPoints[j].y;
1913 xa = pStrokes[i]->pPoints[previous].x;
1914 ya = pStrokes[i]->pPoints[previous].y;
1915 xb = pStrokes[i]->pPoints[next].x;
1916 yb = pStrokes[i]->pPoints[next].y;
1917 theta = atan2(yo - ya, xo - xa);
1918 alpha = atan2(yb - yo, xb - xo) - theta;
1919 if (alpha > 0) alpha -= M_PI;
1920 else alpha += M_PI;
1921 if (_joint == PS_JOIN_MITER && eMiterLimit < fabs(1 / sin(alpha / 2)))
1922 {
1923 _joint = PS_JOIN_BEVEL;
1924 }
1925 if (alpha > 0)
1926 {
1927 pInsidePath = pUpPath;
1928 pOutsidePath = pDownPath;
1929 }
1930 else if (alpha < 0)
1931 {
1932 pInsidePath = pDownPath;
1933 pOutsidePath = pUpPath;
1934 }
1935 else
1936 {
1937 continue;
1938 }
1939 /* Inside angle points */
1940 if (alpha > 0)
1941 {
1942 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1943 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1944 }
1945 else
1946 {
1947 pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
1948 pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
1949 }
1950 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1951 if (alpha > 0)
1952 {
1953 pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
1954 pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
1955 }
1956 else
1957 {
1958 pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
1959 pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
1960 }
1961 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
1962 /* Outside angle point */
1963 switch(_joint)
1964 {
1965 case PS_JOIN_MITER :
1966 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
1967 pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
1968 pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
1969 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
1970 break;
1971 case PS_JOIN_BEVEL :
1972 if (alpha > 0)
1973 {
1974 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1975 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1976 }
1977 else
1978 {
1979 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
1980 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
1981 }
1982 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
1983 if (alpha > 0)
1984 {
1985 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
1986 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
1987 }
1988 else
1989 {
1990 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
1991 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
1992 }
1993 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
1994 break;
1995 case PS_JOIN_ROUND :
1996 default :
1997 if (alpha > 0)
1998 {
1999 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2000 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2001 }
2002 else
2003 {
2004 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2005 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2006 }
2007 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2008 pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2009 pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2010 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2011 if (alpha > 0)
2012 {
2013 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2014 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2015 }
2016 else
2017 {
2018 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2019 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2020 }
2021 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2022 break;
2023 }
2024 }
2025 }
2026 type = add_points( pNewPath, pUpPath->pPoints, pUpPath->numEntriesUsed, PT_LINETO );
2027 type[0] = PT_MOVETO;
2028 reverse_points( pDownPath->pPoints, pDownPath->numEntriesUsed );
2029 type = add_points( pNewPath, pDownPath->pPoints, pDownPath->numEntriesUsed, PT_LINETO );
2030 if (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE) type[0] = PT_MOVETO;
2031
2032 PATH_DestroyGdiPath(pStrokes[i]);
2033 ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2034 PATH_DestroyGdiPath(pUpPath);
2035 ExFreePoolWithTag(pUpPath, TAG_PATH);
2036 PATH_DestroyGdiPath(pDownPath);
2037 ExFreePoolWithTag(pDownPath, TAG_PATH);
2038 }
2039 if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2040
2041 PATH_UnlockPath(flat_path);
2042 PATH_Delete(flat_path->BaseObject.hHmgr);
2043 pNewPath->state = PATH_Closed;
2044 PATH_UnlockPath(pNewPath);
2045 return pNewPath;
2046 }
2047
2048 static
2049 PPATH
2050 FASTCALL
2051 PATH_WidenPath(DC *dc)
2052 {
2053 INT size;
2054 UINT penWidth, penStyle;
2055 DWORD obj_type;
2056 PPATH pPath, pNewPath;
2057 LPEXTLOGPEN elp;
2058 PDC_ATTR pdcattr = dc->pdcattr;
2059
2060 pPath = PATH_LockPath(dc->dclevel.hPath);
2061 if (!pPath)
2062 {
2063 EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2064 return NULL;
2065 }
2066
2067 if (pPath->state != PATH_Closed)
2068 {
2069 DPRINT("PWP 1\n");
2070 PATH_UnlockPath(pPath);
2071 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2072 return NULL;
2073 }
2074
2075 size = GreGetObject(pdcattr->hpen, 0, NULL);
2076 if (!size)
2077 {
2078 DPRINT("PWP 2\n");
2079 PATH_UnlockPath(pPath);
2080 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2081 return NULL;
2082 }
2083
2084 elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
2085 if (elp == NULL)
2086 {
2087 DPRINT("PWP 3\n");
2088 PATH_UnlockPath(pPath);
2089 EngSetLastError(ERROR_OUTOFMEMORY);
2090 return NULL;
2091 }
2092
2093 GreGetObject(pdcattr->hpen, size, elp);
2094
2095 obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
2096 if (obj_type == GDI_OBJECT_TYPE_PEN)
2097 {
2098 penStyle = ((LOGPEN*)elp)->lopnStyle;
2099 }
2100 else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
2101 {
2102 penStyle = elp->elpPenStyle;
2103 }
2104 else
2105 {
2106 DPRINT("PWP 4\n");
2107 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2108 ExFreePoolWithTag(elp, TAG_PATH);
2109 PATH_UnlockPath(pPath);
2110 return NULL;
2111 }
2112
2113 penWidth = elp->elpWidth;
2114 ExFreePoolWithTag(elp, TAG_PATH);
2115
2116 /* The function cannot apply to cosmetic pens */
2117 if (obj_type == GDI_OBJECT_TYPE_EXTPEN &&
2118 (PS_TYPE_MASK & penStyle) == PS_COSMETIC)
2119 {
2120 DPRINT("PWP 5\n");
2121 PATH_UnlockPath(pPath);
2122 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2123 return FALSE;
2124 }
2125
2126 pNewPath = IntGdiWidenPath(pPath, penWidth, penStyle, dc->dclevel.laPath.eMiterLimit);
2127 PATH_UnlockPath(pPath);
2128 return pNewPath;
2129 }
2130
2131 static inline INT int_from_fixed(FIXED f)
2132 {
2133 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2134 }
2135
2136 /**********************************************************************
2137 * PATH_BezierTo
2138 *
2139 * Internally used by PATH_add_outline
2140 */
2141 static
2142 VOID
2143 FASTCALL
2144 PATH_BezierTo(
2145 PPATH pPath,
2146 POINT *lppt,
2147 INT n)
2148 {
2149 if (n < 2) return;
2150
2151 if (n == 2)
2152 {
2153 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2154 }
2155 else if (n == 3)
2156 {
2157 add_points( pPath, lppt, 3, PT_BEZIERTO );
2158 }
2159 else
2160 {
2161 POINT pt[3];
2162 INT i = 0;
2163
2164 pt[2] = lppt[0];
2165 n--;
2166
2167 while (n > 2)
2168 {
2169 pt[0] = pt[2];
2170 pt[1] = lppt[i + 1];
2171 pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2172 pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2173 add_points( pPath, pt, 3, PT_BEZIERTO );
2174 n--;
2175 i++;
2176 }
2177
2178 pt[0] = pt[2];
2179 pt[1] = lppt[i + 1];
2180 pt[2] = lppt[i + 2];
2181 add_points( pPath, pt, 3, PT_BEZIERTO );
2182 }
2183 }
2184
2185 static
2186 BOOL
2187 FASTCALL
2188 PATH_add_outline(
2189 PDC dc,
2190 PPATH pPath,
2191 INT x,
2192 INT y,
2193 TTPOLYGONHEADER *header,
2194 DWORD size)
2195 {
2196 TTPOLYGONHEADER *start;
2197 POINT pt;
2198 BOOL bResult = FALSE;
2199
2200 start = header;
2201
2202 while ((char *)header < (char *)start + size)
2203 {
2204 TTPOLYCURVE *curve;
2205
2206 if (header->dwType != TT_POLYGON_TYPE)
2207 {
2208 DPRINT1("Unknown header type %lu\n", header->dwType);
2209 goto cleanup;
2210 }
2211
2212 pt.x = x + int_from_fixed(header->pfxStart.x);
2213 pt.y = y - int_from_fixed(header->pfxStart.y);
2214 PATH_AddEntry(pPath, &pt, PT_MOVETO);
2215
2216 curve = (TTPOLYCURVE *)(header + 1);
2217
2218 while ((char *)curve < (char *)header + header->cb)
2219 {
2220 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2221
2222 switch(curve->wType)
2223 {
2224 case TT_PRIM_LINE:
2225 {
2226 WORD i;
2227
2228 for (i = 0; i < curve->cpfx; i++)
2229 {
2230 pt.x = x + int_from_fixed(curve->apfx[i].x);
2231 pt.y = y - int_from_fixed(curve->apfx[i].y);
2232 PATH_AddEntry(pPath, &pt, PT_LINETO);
2233 }
2234 break;
2235 }
2236
2237 case TT_PRIM_QSPLINE:
2238 case TT_PRIM_CSPLINE:
2239 {
2240 WORD i;
2241 POINTFX ptfx;
2242 POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2243
2244 if (!pts) goto cleanup;
2245
2246 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2247
2248 pts[0].x = x + int_from_fixed(ptfx.x);
2249 pts[0].y = y - int_from_fixed(ptfx.y);
2250
2251 for (i = 0; i < curve->cpfx; i++)
2252 {
2253 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2254 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2255 }
2256
2257 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2258
2259 ExFreePoolWithTag(pts, TAG_PATH);
2260 break;
2261 }
2262
2263 default:
2264 DPRINT1("Unknown curve type %04x\n", curve->wType);
2265 goto cleanup;
2266 }
2267
2268 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2269 }
2270 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2271 }
2272
2273 bResult = TRUE;
2274
2275 cleanup:
2276 IntGdiCloseFigure(pPath);
2277 return bResult;
2278 }
2279
2280 /**********************************************************************
2281 * PATH_ExtTextOut
2282 */
2283 BOOL
2284 FASTCALL
2285 PATH_ExtTextOut(
2286 PDC dc,
2287 INT x,
2288 INT y,
2289 UINT flags,
2290 const RECTL *lprc,
2291 LPCWSTR str,
2292 UINT count,
2293 const INT *dx)
2294 {
2295 PPATH pPath;
2296 unsigned int idx, ggo_flags = GGO_NATIVE;
2297 POINT offset = {0, 0};
2298
2299 pPath = PATH_LockPath(dc->dclevel.hPath);
2300 if (!pPath)
2301 {
2302 return FALSE;
2303 }
2304
2305 if (pPath->state != PATH_Open)
2306 {
2307 DPRINT1("PATH_ExtTextOut not open\n");
2308 return FALSE;
2309 }
2310
2311 if (!count) return TRUE;
2312 if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
2313
2314 for (idx = 0; idx < count; idx++)
2315 {
2316 MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2317 GLYPHMETRICS gm;
2318 DWORD dwSize;
2319 void *outline;
2320
2321 dwSize = ftGdiGetGlyphOutline(dc,
2322 str[idx],
2323 ggo_flags,
2324 &gm,
2325 0,
2326 NULL,
2327 &identity,
2328 TRUE);
2329 if (dwSize == GDI_ERROR)
2330 {
2331 PATH_UnlockPath(pPath);
2332 return FALSE;
2333 }
2334
2335 /* Add outline only if char is printable */
2336 if (dwSize)
2337 {
2338 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2339 if (!outline)
2340 {
2341 PATH_UnlockPath(pPath);
2342 return FALSE;
2343 }
2344
2345 ftGdiGetGlyphOutline(dc,
2346 str[idx],
2347 ggo_flags,
2348 &gm,
2349 dwSize,
2350 outline,
2351 &identity,
2352 TRUE);
2353
2354 PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize);
2355
2356 ExFreePoolWithTag(outline, TAG_PATH);
2357 }
2358
2359 if (dx)
2360 {
2361 if (flags & ETO_PDY)
2362 {
2363 offset.x += dx[idx * 2];
2364 offset.y += dx[idx * 2 + 1];
2365 }
2366 else
2367 offset.x += dx[idx];
2368 }
2369 else
2370 {
2371 offset.x += gm.gmCellIncX;
2372 offset.y += gm.gmCellIncY;
2373 }
2374 }
2375 PATH_UnlockPath(pPath);
2376 return TRUE;
2377 }
2378
2379
2380 /***********************************************************************
2381 * Exported functions
2382 */
2383
2384 BOOL
2385 APIENTRY
2386 NtGdiAbortPath(HDC hDC)
2387 {
2388 PDC dc = DC_LockDc(hDC);
2389 if (!dc)
2390 {
2391 EngSetLastError(ERROR_INVALID_HANDLE);
2392 return FALSE;
2393 }
2394
2395 if (!dc->dclevel.hPath)
2396 {
2397 DC_UnlockDc(dc);
2398 return TRUE;
2399 }
2400
2401 if (!PATH_Delete(dc->dclevel.hPath))
2402 {
2403 DC_UnlockDc(dc);
2404 return FALSE;
2405 }
2406
2407 dc->dclevel.hPath = 0;
2408 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2409
2410 DC_UnlockDc(dc);
2411 return TRUE;
2412 }
2413
2414 BOOL
2415 APIENTRY
2416 NtGdiBeginPath(HDC hDC)
2417 {
2418 PPATH pPath;
2419 PDC dc;
2420
2421 dc = DC_LockDc(hDC);
2422 if (!dc)
2423 {
2424 EngSetLastError(ERROR_INVALID_HANDLE);
2425 return FALSE;
2426 }
2427
2428 /* If path is already open, do nothing. Check if not Save DC state */
2429 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2430 {
2431 DC_UnlockDc(dc);
2432 return TRUE;
2433 }
2434
2435 if (dc->dclevel.hPath)
2436 {
2437 DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2438 if (!(dc->dclevel.flPath & DCPATH_SAVE))
2439 {
2440 // Remove previous handle.
2441 if (!PATH_Delete(dc->dclevel.hPath))
2442 {
2443 DC_UnlockDc(dc);
2444 return FALSE;
2445 }
2446 }
2447 else
2448 {
2449 // Clear flags and Handle.
2450 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2451 dc->dclevel.hPath = NULL;
2452 }
2453 }
2454 pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL);
2455 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2456 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2457 IntGetCurrentPositionEx(dc, &pPath->pos);
2458 IntLPtoDP( dc, &pPath->pos, 1 );
2459 DPRINT("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
2460 PATH_UnlockPath(pPath);
2461 DC_UnlockDc(dc);
2462
2463 if (!pPath)
2464 {
2465 return FALSE;
2466 }
2467 return TRUE;
2468 }
2469
2470 BOOL
2471 APIENTRY
2472 NtGdiCloseFigure(HDC hDC)
2473 {
2474 BOOL Ret = FALSE; // Default to failure
2475 PDC pDc;
2476 PPATH pPath;
2477
2478 DPRINT("Enter %s\n", __FUNCTION__);
2479
2480 pDc = DC_LockDc(hDC);
2481 if (!pDc)
2482 {
2483 EngSetLastError(ERROR_INVALID_PARAMETER);
2484 return FALSE;
2485 }
2486
2487 pPath = PATH_LockPath(pDc->dclevel.hPath);
2488 if (!pPath)
2489 {
2490 DC_UnlockDc(pDc);
2491 return FALSE;
2492 }
2493
2494 if (pPath->state == PATH_Open)
2495 {
2496 IntGdiCloseFigure(pPath);
2497 Ret = TRUE;
2498 }
2499 else
2500 {
2501 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2502 }
2503
2504 PATH_UnlockPath(pPath);
2505 DC_UnlockDc(pDc);
2506 return Ret;
2507 }
2508
2509 BOOL
2510 APIENTRY
2511 NtGdiEndPath(HDC hDC)
2512 {
2513 BOOL ret = TRUE;
2514 PPATH pPath;
2515 PDC dc;
2516
2517 dc = DC_LockDc(hDC);
2518 if (!dc)
2519 {
2520 EngSetLastError(ERROR_INVALID_HANDLE);
2521 return FALSE;
2522 }
2523
2524 pPath = PATH_LockPath(dc->dclevel.hPath);
2525 if (!pPath)
2526 {
2527 DC_UnlockDc(dc);
2528 return FALSE;
2529 }
2530
2531 /* Check that path is currently being constructed */
2532 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2533 {
2534 DPRINT("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2535 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2536 ret = FALSE;
2537 }
2538 /* Set flag to indicate that path is finished */
2539 else
2540 {
2541 DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2542 pPath->state = PATH_Closed;
2543 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2544 }
2545
2546 PATH_UnlockPath(pPath);
2547 DC_UnlockDc(dc);
2548 return ret;
2549 }
2550
2551 BOOL
2552 APIENTRY
2553 NtGdiFillPath(HDC hDC)
2554 {
2555 BOOL ret = FALSE;
2556 PPATH pPath, pNewPath;
2557 PDC_ATTR pdcattr;
2558 PDC dc;
2559
2560 dc = DC_LockDc(hDC);
2561 if (!dc)
2562 {
2563 EngSetLastError(ERROR_INVALID_PARAMETER);
2564 return FALSE;
2565 }
2566
2567 pPath = PATH_LockPath(dc->dclevel.hPath);
2568 if (!pPath)
2569 {
2570 DC_UnlockDc(dc);
2571 return FALSE;
2572 }
2573
2574 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2575
2576 pdcattr = dc->pdcattr;
2577
2578 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2579 DC_vUpdateLineBrush(dc);
2580
2581 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2582 DC_vUpdateFillBrush(dc);
2583
2584 pNewPath = PATH_FlattenPath(pPath);
2585
2586 if (pNewPath->state != PATH_Closed)
2587 {
2588 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2589 }
2590 else if (pNewPath->numEntriesUsed)
2591 {
2592 ret = PATH_FillPath(dc, pNewPath);
2593 }
2594 else ret = TRUE;
2595
2596 PATH_UnlockPath(pNewPath);
2597 PATH_Delete(pNewPath->BaseObject.hHmgr);
2598
2599 PATH_UnlockPath(pPath);
2600 PATH_Delete(pPath->BaseObject.hHmgr);
2601 dc->dclevel.hPath = 0;
2602 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2603
2604 DC_vFinishBlit(dc, NULL);
2605 DC_UnlockDc(dc);
2606 return ret;
2607 }
2608
2609 BOOL
2610 APIENTRY
2611 NtGdiFlattenPath(HDC hDC)
2612 {
2613 BOOL Ret = FALSE;
2614 DC *pDc;
2615 PPATH pPath, pNewPath = NULL;
2616
2617 DPRINT("Enter %s\n", __FUNCTION__);
2618
2619 pDc = DC_LockDc(hDC);
2620 if (!pDc)
2621 {
2622 EngSetLastError(ERROR_INVALID_HANDLE);
2623 return FALSE;
2624 }
2625
2626 pPath = PATH_LockPath(pDc->dclevel.hPath);
2627 if (!pPath)
2628 {
2629 EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2630 DC_UnlockDc(pDc);
2631 return FALSE;
2632 }
2633
2634 if (pPath->state == PATH_Closed)
2635 {
2636 pNewPath = PATH_FlattenPath(pPath);
2637 }
2638
2639 PATH_UnlockPath(pPath);
2640
2641 if (pNewPath)
2642 {
2643 PATH_Delete(pDc->dclevel.hPath);
2644 pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr;
2645 PATH_UnlockPath(pNewPath);
2646 Ret = TRUE;
2647 }
2648
2649 DC_UnlockDc(pDc);
2650 return Ret;
2651 }
2652
2653 _Success_(return != FALSE)
2654 BOOL
2655 APIENTRY
2656 NtGdiGetMiterLimit(
2657 _In_ HDC hdc,
2658 _Out_ PDWORD pdwOut)
2659 {
2660 DC *pDc;
2661 BOOL bResult = TRUE;
2662
2663 if (!(pDc = DC_LockDc(hdc)))
2664 {
2665 EngSetLastError(ERROR_INVALID_PARAMETER);
2666 return FALSE;
2667 }
2668
2669 _SEH2_TRY
2670 {
2671 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2672 *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2673 }
2674 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2675 {
2676 SetLastNtError(_SEH2_GetExceptionCode());
2677 bResult = FALSE;
2678 }
2679 _SEH2_END;
2680
2681 DC_UnlockDc(pDc);
2682 return bResult;
2683
2684 }
2685
2686 INT
2687 APIENTRY
2688 NtGdiGetPath(
2689 HDC hDC,
2690 LPPOINT Points,
2691 LPBYTE Types,
2692 INT nSize)
2693 {
2694 INT ret = -1;
2695 PPATH pPath;
2696
2697 DC *dc = DC_LockDc(hDC);
2698 DPRINT("NtGdiGetPath start\n");
2699 if (!dc)
2700 {
2701 DPRINT1("Can't lock dc!\n");
2702 EngSetLastError(ERROR_INVALID_PARAMETER);
2703 return -1;
2704 }
2705
2706 pPath = PATH_LockPath(dc->dclevel.hPath);
2707 if (!pPath)
2708 {
2709 DC_UnlockDc(dc);
2710 return -1;
2711 }
2712
2713 if (pPath->state != PATH_Closed)
2714 {
2715 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2716 goto done;
2717 }
2718
2719 if (nSize == 0)
2720 {
2721 ret = pPath->numEntriesUsed;
2722 }
2723 else if (nSize < pPath->numEntriesUsed)
2724 {
2725 EngSetLastError(ERROR_INVALID_PARAMETER);
2726 goto done;
2727 }
2728 else
2729 {
2730 _SEH2_TRY
2731 {
2732 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2733 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2734
2735 /* Convert the points to logical coordinates */
2736 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2737 {
2738 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2739 _SEH2_LEAVE;
2740 }
2741
2742 ret = pPath->numEntriesUsed;
2743 }
2744 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2745 {
2746 SetLastNtError(_SEH2_GetExceptionCode());
2747 }
2748 _SEH2_END
2749 }
2750
2751 done:
2752 DPRINT("NtGdiGetPath exit %d\n",ret);
2753 PATH_UnlockPath(pPath);
2754 DC_UnlockDc(dc);
2755 return ret;
2756 }
2757
2758 HRGN
2759 APIENTRY
2760 NtGdiPathToRegion(HDC hDC)
2761 {
2762 PPATH pPath, pNewPath;
2763 HRGN hrgnRval = 0;
2764 int Ret;
2765 PREGION Rgn;
2766 DC *pDc;
2767 PDC_ATTR pdcattr;
2768
2769 DPRINT("Enter %s\n", __FUNCTION__);
2770
2771 pDc = DC_LockDc(hDC);
2772 if (!pDc)
2773 {
2774 DPRINT("Failed to lock DC %p\n", hDC);
2775 EngSetLastError(ERROR_INVALID_PARAMETER);
2776 return NULL;
2777 }
2778
2779 pdcattr = pDc->pdcattr;
2780
2781 pPath = PATH_LockPath(pDc->dclevel.hPath);
2782 if (!pPath)
2783 {
2784 DPRINT("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2785 DC_UnlockDc(pDc);
2786 return NULL;
2787 }
2788
2789 if (pPath->state != PATH_Closed)
2790 {
2791 // FIXME: Check that setlasterror is being called correctly
2792 DPRINT("Path is not closed!\n");
2793 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2794 }
2795 else
2796 {
2797 /* Create the region and fill it with the path strokes */
2798 Rgn = REGION_AllocUserRgnWithHandle(1);
2799 if (!Rgn)
2800 {
2801 DPRINT("Failed to allocate a region\n");
2802 PATH_UnlockPath(pPath);
2803 DC_UnlockDc(pDc);
2804 return NULL;
2805 }
2806 hrgnRval = Rgn->BaseObject.hHmgr;
2807
2808 pNewPath = PATH_FlattenPath(pPath);
2809
2810 Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn);
2811
2812 PATH_UnlockPath(pNewPath);
2813 PATH_Delete(pNewPath->BaseObject.hHmgr);
2814
2815 if (!Ret)
2816 {
2817 DPRINT("PATH_PathToRegion failed\n");
2818 REGION_Delete(Rgn);
2819 hrgnRval = NULL;
2820 }
2821 else
2822 REGION_UnlockRgn(Rgn);
2823 }
2824
2825 PATH_UnlockPath(pPath);
2826 PATH_Delete(pDc->dclevel.hPath);
2827 pDc->dclevel.hPath = NULL;
2828 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2829
2830 DC_UnlockDc(pDc);
2831 return hrgnRval;
2832 }
2833
2834 BOOL
2835 APIENTRY
2836 NtGdiSetMiterLimit(
2837 IN HDC hdc,
2838 IN DWORD dwNew,
2839 IN OUT OPTIONAL PDWORD pdwOut)
2840 {
2841 DC *pDc;
2842 gxf_long worker, worker1;
2843 BOOL bResult = TRUE;
2844
2845 if (!(pDc = DC_LockDc(hdc)))
2846 {
2847 EngSetLastError(ERROR_INVALID_PARAMETER);
2848 return FALSE;
2849 }
2850
2851 worker.l = dwNew;
2852 worker1.f = pDc->dclevel.laPath.eMiterLimit;
2853 pDc->dclevel.laPath.eMiterLimit = worker.f;
2854
2855 if (pdwOut)
2856 {
2857 _SEH2_TRY
2858 {
2859 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2860 *pdwOut = worker1.l;
2861 }
2862 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2863 {
2864 SetLastNtError(_SEH2_GetExceptionCode());
2865 bResult = FALSE;
2866 }
2867 _SEH2_END;
2868 }
2869
2870 DC_UnlockDc(pDc);
2871 return bResult;
2872 }
2873
2874 BOOL
2875 APIENTRY
2876 NtGdiStrokeAndFillPath(HDC hDC)
2877 {
2878 DC *pDc;
2879 PDC_ATTR pdcattr;
2880 PPATH pPath, pNewPath;
2881 BOOL bRet = FALSE;
2882
2883 DPRINT("Enter %s\n", __FUNCTION__);
2884
2885 if (!(pDc = DC_LockDc(hDC)))
2886 {
2887 EngSetLastError(ERROR_INVALID_PARAMETER);
2888 return FALSE;
2889 }
2890 pPath = PATH_LockPath(pDc->dclevel.hPath);
2891 if (!pPath)
2892 {
2893 DC_UnlockDc(pDc);
2894 return FALSE;
2895 }
2896
2897 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2898
2899 pdcattr = pDc->pdcattr;
2900
2901 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2902 DC_vUpdateFillBrush(pDc);
2903
2904 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2905 DC_vUpdateLineBrush(pDc);
2906
2907 pNewPath = PATH_FlattenPath(pPath);
2908
2909 if (pNewPath->state != PATH_Closed)
2910 {
2911 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2912 }
2913 else if (pNewPath->numEntriesUsed)
2914 {
2915 bRet = PATH_FillPath(pDc, pNewPath);
2916 if (bRet) bRet = PATH_StrokePath(pDc, pNewPath);
2917 }
2918 else bRet = TRUE;
2919
2920 PATH_UnlockPath(pNewPath);
2921 PATH_Delete(pNewPath->BaseObject.hHmgr);
2922
2923 PATH_UnlockPath(pPath);
2924 PATH_Delete(pPath->BaseObject.hHmgr);
2925 pDc->dclevel.hPath = 0;
2926 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2927
2928 DC_vFinishBlit(pDc, NULL);
2929 DC_UnlockDc(pDc);
2930 return bRet;
2931 }
2932
2933 BOOL
2934 APIENTRY
2935 NtGdiStrokePath(HDC hDC)
2936 {
2937 DC *pDc;
2938 PDC_ATTR pdcattr;
2939 PPATH pPath, pNewPath;
2940 BOOL bRet = FALSE;
2941
2942 DPRINT("Enter %s\n", __FUNCTION__);
2943
2944 if (!(pDc = DC_LockDc(hDC)))
2945 {
2946 EngSetLastError(ERROR_INVALID_PARAMETER);
2947 return FALSE;
2948 }
2949
2950 pPath = PATH_LockPath(pDc->dclevel.hPath);
2951 if (!pPath)
2952 {
2953 DC_UnlockDc(pDc);
2954 return FALSE;
2955 }
2956
2957 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2958
2959 pdcattr = pDc->pdcattr;
2960
2961 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2962 DC_vUpdateLineBrush(pDc);
2963
2964 pNewPath = PATH_FlattenPath(pPath);
2965
2966 if (pNewPath->state != PATH_Closed)
2967 {
2968 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2969 }
2970 else bRet = PATH_StrokePath(pDc, pNewPath);
2971
2972 PATH_UnlockPath(pNewPath);
2973 PATH_Delete(pNewPath->BaseObject.hHmgr);
2974
2975 DC_vFinishBlit(pDc, NULL);
2976
2977 PATH_UnlockPath(pPath);
2978 PATH_Delete(pPath->BaseObject.hHmgr);
2979 pDc->dclevel.hPath = 0;
2980 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2981
2982 DC_UnlockDc(pDc);
2983 return bRet;
2984 }
2985
2986 BOOL
2987 APIENTRY
2988 NtGdiWidenPath(HDC hDC)
2989 {
2990 PPATH pPath;
2991 BOOL Ret = FALSE;
2992 PDC pdc = DC_LockDc(hDC);
2993 DPRINT("NtGdiWidenPat Enter\n");
2994 if (!pdc)
2995 {
2996 EngSetLastError(ERROR_INVALID_PARAMETER);
2997 return FALSE;
2998 }
2999
3000 pPath = PATH_WidenPath(pdc);
3001 if (pPath)
3002 {
3003 DPRINT("WindenPath New Path\n");
3004 PATH_Delete(pdc->dclevel.hPath);
3005 pdc->dclevel.hPath = pPath->BaseObject.hHmgr;
3006 Ret = TRUE;
3007 }
3008 DC_UnlockDc(pdc);
3009 DPRINT("NtGdiWidenPat Ret %d\n",Ret);
3010 return Ret;
3011 }
3012
3013 /* EOF */