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