f4cc24951a08fcd0cfd03173a379170a0f1d8750
[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 INT mapMode, graphicsMode;
1462 SIZE ptViewportExt, ptWindowExt;
1463 POINTL ptViewportOrg, ptWindowOrg;
1464 XFORML xform;
1465 PREGION Rgn;
1466 PDC_ATTR pdcattr = dc->pdcattr;
1467
1468 /* Allocate a temporary region */
1469 Rgn = IntSysCreateRectpRgn(0, 0, 0, 0);
1470 if (!Rgn)
1471 {
1472 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1473 return FALSE;
1474 }
1475
1476 if (!PATH_PathToRegion(pPath, pdcattr->jFillMode, Rgn))
1477 {
1478 DPRINT("PFP : Fail P2R\n");
1479 /* EngSetLastError ? */
1480 REGION_Delete(Rgn);
1481 return FALSE;
1482 }
1483
1484 /* Since PaintRgn interprets the region as being in logical coordinates
1485 * but the points we store for the path are already in device
1486 * coordinates, we have to set the mapping mode to MM_TEXT temporarily.
1487 * Using SaveDC to save information about the mapping mode / world
1488 * transform would be easier but would require more overhead, especially
1489 * now that SaveDC saves the current path.
1490 */
1491
1492 /* Save the information about the old mapping mode */
1493 mapMode = pdcattr->iMapMode;
1494 ptViewportExt = pdcattr->szlViewportExt;
1495 ptViewportOrg = pdcattr->ptlViewportOrg;
1496 ptWindowExt = pdcattr->szlWindowExt;
1497 ptWindowOrg = pdcattr->ptlWindowOrg;
1498
1499 /* Save world transform
1500 * NB: The Windows documentation on world transforms would lead one to
1501 * believe that this has to be done only in GM_ADVANCED; however, my
1502 * tests show that resetting the graphics mode to GM_COMPATIBLE does
1503 * not reset the world transform.
1504 */
1505 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1506
1507 /* Set MM_TEXT */
1508 IntGdiSetMapMode(dc, MM_TEXT);
1509 pdcattr->ptlViewportOrg.x = 0;
1510 pdcattr->ptlViewportOrg.y = 0;
1511 pdcattr->ptlWindowOrg.x = 0;
1512 pdcattr->ptlWindowOrg.y = 0;
1513
1514 graphicsMode = pdcattr->iGraphicsMode;
1515 pdcattr->iGraphicsMode = GM_ADVANCED;
1516 GreModifyWorldTransform(dc, &xform, MWT_IDENTITY);
1517 pdcattr->iGraphicsMode = graphicsMode;
1518
1519 /* Paint the region */
1520 IntGdiPaintRgn(dc, Rgn);
1521 REGION_Delete(Rgn);
1522 /* Restore the old mapping mode */
1523 IntGdiSetMapMode(dc, mapMode);
1524 pdcattr->szlViewportExt = ptViewportExt;
1525 pdcattr->ptlViewportOrg = ptViewportOrg;
1526 pdcattr->szlWindowExt = ptWindowExt;
1527 pdcattr->ptlWindowOrg = ptWindowOrg;
1528
1529 /* Go to GM_ADVANCED temporarily to restore the world transform */
1530 graphicsMode = pdcattr->iGraphicsMode;
1531 pdcattr->iGraphicsMode = GM_ADVANCED;
1532 GreModifyWorldTransform(dc, &xform, MWT_SET);
1533 pdcattr->iGraphicsMode = graphicsMode;
1534 return TRUE;
1535 }
1536
1537 BOOL
1538 FASTCALL
1539 PATH_StrokePath(
1540 DC *dc,
1541 PPATH pPath)
1542 {
1543 BOOL ret = FALSE;
1544 INT i = 0;
1545 INT nLinePts, nAlloc;
1546 POINT *pLinePts = NULL;
1547 POINT ptViewportOrg, ptWindowOrg;
1548 SIZE szViewportExt, szWindowExt;
1549 DWORD mapMode, graphicsMode;
1550 XFORM xform;
1551 PDC_ATTR pdcattr = dc->pdcattr;
1552
1553 DPRINT("Enter %s\n", __FUNCTION__);
1554
1555 /* Save the mapping mode info */
1556 mapMode = pdcattr->iMapMode;
1557
1558 szViewportExt = *DC_pszlViewportExt(dc);
1559 ptViewportOrg = dc->pdcattr->ptlViewportOrg;
1560 szWindowExt = dc->pdcattr->szlWindowExt;
1561 ptWindowOrg = dc->pdcattr->ptlWindowOrg;
1562
1563 MatrixS2XForm(&xform, &dc->pdcattr->mxWorldToPage);
1564
1565 /* Set MM_TEXT */
1566 pdcattr->iMapMode = MM_TEXT;
1567 pdcattr->ptlViewportOrg.x = 0;
1568 pdcattr->ptlViewportOrg.y = 0;
1569 pdcattr->ptlWindowOrg.x = 0;
1570 pdcattr->ptlWindowOrg.y = 0;
1571 graphicsMode = pdcattr->iGraphicsMode;
1572 pdcattr->iGraphicsMode = GM_ADVANCED;
1573 GreModifyWorldTransform(dc, (XFORML*)&xform, MWT_IDENTITY);
1574 pdcattr->iGraphicsMode = graphicsMode;
1575
1576 /* Allocate enough memory for the worst case without beziers (one PT_MOVETO
1577 * and the rest PT_LINETO with PT_CLOSEFIGURE at the end) plus some buffer
1578 * space in case we get one to keep the number of reallocations small. */
1579 nAlloc = pPath->numEntriesUsed + 1 + 300;
1580 pLinePts = ExAllocatePoolWithTag(PagedPool, nAlloc * sizeof(POINT), TAG_PATH);
1581 if (!pLinePts)
1582 {
1583 DPRINT1("Can't allocate pool!\n");
1584 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1585 goto end;
1586 }
1587 nLinePts = 0;
1588
1589 for (i = 0; i < pPath->numEntriesUsed; i++)
1590 {
1591 if ((i == 0 || (pPath->pFlags[i - 1] & PT_CLOSEFIGURE))
1592 && (pPath->pFlags[i] != PT_MOVETO))
1593 {
1594 DPRINT1("Expected PT_MOVETO %s, got path flag %d\n",
1595 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1596 (INT)pPath->pFlags[i]);
1597 goto end;
1598 }
1599
1600 switch(pPath->pFlags[i])
1601 {
1602 case PT_MOVETO:
1603 DPRINT("Got PT_MOVETO (%ld, %ld)\n",
1604 pPath->pPoints[i].x, pPath->pPoints[i].y);
1605 if (nLinePts >= 2) IntGdiPolyline(dc, pLinePts, nLinePts);
1606 nLinePts = 0;
1607 pLinePts[nLinePts++] = pPath->pPoints[i];
1608 break;
1609 case PT_LINETO:
1610 case (PT_LINETO | PT_CLOSEFIGURE):
1611 DPRINT("Got PT_LINETO (%ld, %ld)\n",
1612 pPath->pPoints[i].x, pPath->pPoints[i].y);
1613 pLinePts[nLinePts++] = pPath->pPoints[i];
1614 break;
1615 case PT_BEZIERTO:
1616 DPRINT("Got PT_BEZIERTO\n");
1617 if (pPath->pFlags[i + 1] != PT_BEZIERTO ||
1618 (pPath->pFlags[i + 2] & ~PT_CLOSEFIGURE) != PT_BEZIERTO)
1619 {
1620 DPRINT1("Path didn't contain 3 successive PT_BEZIERTOs\n");
1621 ret = FALSE;
1622 goto end;
1623 }
1624 else
1625 {
1626 INT nBzrPts, nMinAlloc;
1627 POINT *pBzrPts = GDI_Bezier(&pPath->pPoints[i - 1], 4, &nBzrPts);
1628 /* Make sure we have allocated enough memory for the lines of
1629 * this bezier and the rest of the path, assuming we won't get
1630 * another one (since we won't reallocate again then). */
1631 nMinAlloc = nLinePts + (pPath->numEntriesUsed - i) + nBzrPts;
1632 if (nAlloc < nMinAlloc)
1633 {
1634 // Reallocate memory
1635
1636 POINT *Realloc = NULL;
1637 nAlloc = nMinAlloc * 2;
1638
1639 Realloc = ExAllocatePoolWithTag(PagedPool,
1640 nAlloc * sizeof(POINT),
1641 TAG_PATH);
1642
1643 if (!Realloc)
1644 {
1645 DPRINT1("Can't allocate pool!\n");
1646 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1647 goto end;
1648 }
1649
1650 memcpy(Realloc, pLinePts, nLinePts * sizeof(POINT));
1651 ExFreePoolWithTag(pLinePts, TAG_PATH);
1652 pLinePts = Realloc;
1653 }
1654 memcpy(&pLinePts[nLinePts], &pBzrPts[1], (nBzrPts - 1) * sizeof(POINT));
1655 nLinePts += nBzrPts - 1;
1656 ExFreePoolWithTag(pBzrPts, TAG_BEZIER);
1657 i += 2;
1658 }
1659 break;
1660 default:
1661 DPRINT1("Got path flag %d (not supported)\n", (INT)pPath->pFlags[i]);
1662 goto end;
1663 }
1664
1665 if (pPath->pFlags[i] & PT_CLOSEFIGURE)
1666 {
1667 pLinePts[nLinePts++] = pLinePts[0];
1668 }
1669 }
1670 if (nLinePts >= 2)
1671 IntGdiPolyline(dc, pLinePts, nLinePts);
1672
1673 ret = TRUE;
1674
1675 end:
1676 if (pLinePts) ExFreePoolWithTag(pLinePts, TAG_PATH);
1677
1678 /* Restore the old mapping mode */
1679 pdcattr->iMapMode = mapMode;
1680 pdcattr->szlWindowExt.cx = szWindowExt.cx;
1681 pdcattr->szlWindowExt.cy = szWindowExt.cy;
1682 pdcattr->ptlWindowOrg.x = ptWindowOrg.x;
1683 pdcattr->ptlWindowOrg.y = ptWindowOrg.y;
1684
1685 pdcattr->szlViewportExt.cx = szViewportExt.cx;
1686 pdcattr->szlViewportExt.cy = szViewportExt.cy;
1687 pdcattr->ptlViewportOrg.x = ptViewportOrg.x;
1688 pdcattr->ptlViewportOrg.y = ptViewportOrg.y;
1689
1690 /* Restore the world transform */
1691 XForm2MatrixS(&dc->pdcattr->mxWorldToPage, &xform);
1692
1693 /* If we've moved the current point then get its new position
1694 which will be in device (MM_TEXT) co-ords, convert it to
1695 logical co-ords and re-set it. This basically updates
1696 dc->CurPosX|Y so that their values are in the correct mapping
1697 mode.
1698 */
1699 if (i > 0)
1700 {
1701 POINT pt;
1702 IntGetCurrentPositionEx(dc, &pt);
1703 IntDPtoLP(dc, &pt, 1);
1704 IntGdiMoveToEx(dc, pt.x, pt.y, NULL);
1705 }
1706 DPRINT("Leave %s, ret=%d\n", __FUNCTION__, ret);
1707 return ret;
1708 }
1709
1710 #define round(x) ((int)((x)>0?(x)+0.5:(x)-0.5))
1711
1712 static
1713 PPATH
1714 FASTCALL
1715 PATH_WidenPath(DC *dc)
1716 {
1717 INT i, j, numStrokes, numOldStrokes, penWidth, penWidthIn, penWidthOut, size, penStyle;
1718 PPATH pPath, flat_path, pNewPath, *pStrokes = NULL, *pOldStrokes, pUpPath, pDownPath;
1719 EXTLOGPEN *elp;
1720 BYTE *type;
1721 DWORD obj_type, joint, endcap, penType;
1722 PDC_ATTR pdcattr = dc->pdcattr;
1723
1724 pPath = PATH_LockPath(dc->dclevel.hPath);
1725 if (!pPath)
1726 {
1727 EngSetLastError( ERROR_CAN_NOT_COMPLETE );
1728 return NULL;
1729 }
1730
1731 if (pPath->state != PATH_Closed)
1732 {
1733 DPRINT("PWP 1\n");
1734 PATH_UnlockPath(pPath);
1735 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1736 return NULL;
1737 }
1738
1739 size = GreGetObject(pdcattr->hpen, 0, NULL);
1740 if (!size)
1741 {
1742 DPRINT("PWP 2\n");
1743 PATH_UnlockPath(pPath);
1744 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1745 return FALSE;
1746 }
1747
1748 elp = ExAllocatePoolWithTag(PagedPool, size, TAG_PATH);
1749 if (elp == NULL)
1750 {
1751 DPRINT("PWP 3\n");
1752 PATH_UnlockPath(pPath);
1753 EngSetLastError(ERROR_OUTOFMEMORY);
1754 return FALSE;
1755 }
1756
1757 GreGetObject(pdcattr->hpen, size, elp);
1758
1759 obj_type = GDI_HANDLE_GET_TYPE(pdcattr->hpen);
1760 if (obj_type == GDI_OBJECT_TYPE_PEN)
1761 {
1762 penStyle = ((LOGPEN*)elp)->lopnStyle;
1763 }
1764 else if (obj_type == GDI_OBJECT_TYPE_EXTPEN)
1765 {
1766 penStyle = elp->elpPenStyle;
1767 }
1768 else
1769 {
1770 DPRINT("PWP 4\n");
1771 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1772 ExFreePoolWithTag(elp, TAG_PATH);
1773 PATH_UnlockPath(pPath);
1774 return FALSE;
1775 }
1776
1777 penWidth = elp->elpWidth;
1778 ExFreePoolWithTag(elp, TAG_PATH);
1779
1780 endcap = (PS_ENDCAP_MASK & penStyle);
1781 joint = (PS_JOIN_MASK & penStyle);
1782 penType = (PS_TYPE_MASK & penStyle);
1783
1784 /* The function cannot apply to cosmetic pens */
1785 if (obj_type == GDI_OBJECT_TYPE_EXTPEN && penType == PS_COSMETIC)
1786 {
1787 DPRINT("PWP 5\n");
1788 PATH_UnlockPath(pPath);
1789 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
1790 return FALSE;
1791 }
1792
1793 if (!(flat_path = PATH_FlattenPath(pPath)))
1794 {
1795 PATH_UnlockPath(pPath);
1796 return NULL;
1797 }
1798 PATH_UnlockPath(pPath);
1799
1800 penWidthIn = penWidth / 2;
1801 penWidthOut = penWidth / 2;
1802 if (penWidthIn + penWidthOut < penWidth)
1803 penWidthOut++;
1804
1805 numStrokes = 0;
1806
1807 for (i = 0, j = 0; i < flat_path->numEntriesUsed; i++, j++)
1808 {
1809 POINT point;
1810 if ((i == 0 || (flat_path->pFlags[i - 1] & PT_CLOSEFIGURE)) &&
1811 (flat_path->pFlags[i] != PT_MOVETO))
1812 {
1813 DPRINT1("Expected PT_MOVETO %s, got path flag %c\n",
1814 i == 0 ? "as first point" : "after PT_CLOSEFIGURE",
1815 flat_path->pFlags[i]);
1816 if (pStrokes)
1817 ExFreePoolWithTag(pStrokes, TAG_PATH);
1818 PATH_UnlockPath(flat_path);
1819 PATH_Delete(flat_path->BaseObject.hHmgr);
1820 return FALSE;
1821 }
1822 switch(flat_path->pFlags[i])
1823 {
1824 case PT_MOVETO:
1825 if (numStrokes > 0)
1826 {
1827 pStrokes[numStrokes - 1]->state = PATH_Closed;
1828 }
1829 numOldStrokes = numStrokes;
1830 numStrokes++;
1831 j = 0;
1832 if (numStrokes == 1)
1833 pStrokes = ExAllocatePoolWithTag(PagedPool, sizeof(*pStrokes), TAG_PATH);
1834 else
1835 {
1836 pOldStrokes = pStrokes; // Save old pointer.
1837 pStrokes = ExAllocatePoolWithTag(PagedPool, numStrokes * sizeof(*pStrokes), TAG_PATH);
1838 if (!pStrokes)
1839 {
1840 PATH_UnlockPath(flat_path);
1841 PATH_Delete(flat_path->BaseObject.hHmgr);
1842 return FALSE;
1843 }
1844 RtlCopyMemory(pStrokes, pOldStrokes, numOldStrokes * sizeof(PPATH));
1845 ExFreePoolWithTag(pOldStrokes, TAG_PATH); // Free old pointer.
1846 }
1847 if (!pStrokes)
1848 {
1849 PATH_UnlockPath(flat_path);
1850 PATH_Delete(flat_path->BaseObject.hHmgr);
1851 return FALSE;
1852 }
1853 pStrokes[numStrokes - 1] = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1854 if (!pStrokes[numStrokes - 1])
1855 {
1856 ASSERT(FALSE); // FIXME
1857 }
1858 PATH_InitGdiPath(pStrokes[numStrokes - 1]);
1859 pStrokes[numStrokes - 1]->state = PATH_Open;
1860 case PT_LINETO:
1861 case (PT_LINETO | PT_CLOSEFIGURE):
1862 point.x = flat_path->pPoints[i].x;
1863 point.y = flat_path->pPoints[i].y;
1864 PATH_AddEntry(pStrokes[numStrokes - 1], &point, flat_path->pFlags[i]);
1865 break;
1866 case PT_BEZIERTO:
1867 /* Should never happen because of the FlattenPath call */
1868 DPRINT1("Should never happen\n");
1869 break;
1870 default:
1871 DPRINT1("Got path flag %c\n", flat_path->pFlags[i]);
1872 if (pStrokes)
1873 ExFreePoolWithTag(pStrokes, TAG_PATH);
1874 PATH_UnlockPath(flat_path);
1875 PATH_Delete(flat_path->BaseObject.hHmgr);
1876 return FALSE;
1877 }
1878 }
1879
1880 pNewPath = PATH_CreatePath( flat_path->numEntriesUsed );
1881
1882 for (i = 0; i < numStrokes; i++)
1883 {
1884 pUpPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1885 PATH_InitGdiPath(pUpPath);
1886 pUpPath->state = PATH_Open;
1887 pDownPath = ExAllocatePoolWithTag(PagedPool, sizeof(PATH), TAG_PATH);
1888 PATH_InitGdiPath(pDownPath);
1889 pDownPath->state = PATH_Open;
1890
1891 for (j = 0; j < pStrokes[i]->numEntriesUsed; j++)
1892 {
1893 /* Beginning or end of the path if not closed */
1894 if ((!(pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE)) && (j == 0 || j == pStrokes[i]->numEntriesUsed - 1))
1895 {
1896 /* Compute segment angle */
1897 double xo, yo, xa, ya, theta;
1898 POINT pt;
1899 FLOAT_POINT corners[2];
1900 if (j == 0)
1901 {
1902 xo = pStrokes[i]->pPoints[j].x;
1903 yo = pStrokes[i]->pPoints[j].y;
1904 xa = pStrokes[i]->pPoints[1].x;
1905 ya = pStrokes[i]->pPoints[1].y;
1906 }
1907 else
1908 {
1909 xa = pStrokes[i]->pPoints[j - 1].x;
1910 ya = pStrokes[i]->pPoints[j - 1].y;
1911 xo = pStrokes[i]->pPoints[j].x;
1912 yo = pStrokes[i]->pPoints[j].y;
1913 }
1914 theta = atan2(ya - yo, xa - xo);
1915 switch(endcap)
1916 {
1917 case PS_ENDCAP_SQUARE :
1918 pt.x = xo + round(sqrt(2) * penWidthOut * cos(M_PI_4 + theta));
1919 pt.y = yo + round(sqrt(2) * penWidthOut * sin(M_PI_4 + theta));
1920 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1921 pt.x = xo + round(sqrt(2) * penWidthIn * cos(- M_PI_4 + theta));
1922 pt.y = yo + round(sqrt(2) * penWidthIn * sin(- M_PI_4 + theta));
1923 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1924 break;
1925 case PS_ENDCAP_FLAT :
1926 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
1927 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
1928 PATH_AddEntry(pUpPath, &pt, (j == 0 ? PT_MOVETO : PT_LINETO));
1929 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
1930 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
1931 PATH_AddEntry(pUpPath, &pt, PT_LINETO);
1932 break;
1933 case PS_ENDCAP_ROUND :
1934 default :
1935 corners[0].x = xo - penWidthIn;
1936 corners[0].y = yo - penWidthIn;
1937 corners[1].x = xo + penWidthOut;
1938 corners[1].y = yo + penWidthOut;
1939 PATH_DoArcPart(pUpPath , corners, theta + M_PI_2 , theta + 3 * M_PI_4, (j == 0 ? PT_MOVETO : FALSE));
1940 PATH_DoArcPart(pUpPath , corners, theta + 3 * M_PI_4 , theta + M_PI, FALSE);
1941 PATH_DoArcPart(pUpPath , corners, theta + M_PI, theta + 5 * M_PI_4, FALSE);
1942 PATH_DoArcPart(pUpPath , corners, theta + 5 * M_PI_4 , theta + 3 * M_PI_2, FALSE);
1943 break;
1944 }
1945 }
1946 /* Corpse of the path */
1947 else
1948 {
1949 /* Compute angle */
1950 INT previous, next;
1951 double xa, ya, xb, yb, xo, yo;
1952 double alpha, theta, miterWidth;
1953 DWORD _joint = joint;
1954 POINT pt;
1955 PPATH pInsidePath, pOutsidePath;
1956 if (j > 0 && j < pStrokes[i]->numEntriesUsed - 1)
1957 {
1958 previous = j - 1;
1959 next = j + 1;
1960 }
1961 else if (j == 0)
1962 {
1963 previous = pStrokes[i]->numEntriesUsed - 1;
1964 next = j + 1;
1965 }
1966 else
1967 {
1968 previous = j - 1;
1969 next = 0;
1970 }
1971 xo = pStrokes[i]->pPoints[j].x;
1972 yo = pStrokes[i]->pPoints[j].y;
1973 xa = pStrokes[i]->pPoints[previous].x;
1974 ya = pStrokes[i]->pPoints[previous].y;
1975 xb = pStrokes[i]->pPoints[next].x;
1976 yb = pStrokes[i]->pPoints[next].y;
1977 theta = atan2(yo - ya, xo - xa);
1978 alpha = atan2(yb - yo, xb - xo) - theta;
1979 if (alpha > 0) alpha -= M_PI;
1980 else alpha += M_PI;
1981 if (_joint == PS_JOIN_MITER && dc->dclevel.laPath.eMiterLimit < fabs(1 / sin(alpha / 2)))
1982 {
1983 _joint = PS_JOIN_BEVEL;
1984 }
1985 if (alpha > 0)
1986 {
1987 pInsidePath = pUpPath;
1988 pOutsidePath = pDownPath;
1989 }
1990 else if (alpha < 0)
1991 {
1992 pInsidePath = pDownPath;
1993 pOutsidePath = pUpPath;
1994 }
1995 else
1996 {
1997 continue;
1998 }
1999 /* Inside angle points */
2000 if (alpha > 0)
2001 {
2002 pt.x = xo - round(penWidthIn * cos(theta + M_PI_2));
2003 pt.y = yo - round(penWidthIn * sin(theta + M_PI_2));
2004 }
2005 else
2006 {
2007 pt.x = xo + round(penWidthIn * cos(theta + M_PI_2));
2008 pt.y = yo + round(penWidthIn * sin(theta + M_PI_2));
2009 }
2010 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2011 if (alpha > 0)
2012 {
2013 pt.x = xo + round(penWidthIn * cos(M_PI_2 + alpha + theta));
2014 pt.y = yo + round(penWidthIn * sin(M_PI_2 + alpha + theta));
2015 }
2016 else
2017 {
2018 pt.x = xo - round(penWidthIn * cos(M_PI_2 + alpha + theta));
2019 pt.y = yo - round(penWidthIn * sin(M_PI_2 + alpha + theta));
2020 }
2021 PATH_AddEntry(pInsidePath, &pt, PT_LINETO);
2022 /* Outside angle point */
2023 switch(_joint)
2024 {
2025 case PS_JOIN_MITER :
2026 miterWidth = fabs(penWidthOut / cos(M_PI_2 - fabs(alpha) / 2));
2027 pt.x = xo + round(miterWidth * cos(theta + alpha / 2));
2028 pt.y = yo + round(miterWidth * sin(theta + alpha / 2));
2029 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2030 break;
2031 case PS_JOIN_BEVEL :
2032 if (alpha > 0)
2033 {
2034 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2035 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2036 }
2037 else
2038 {
2039 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2040 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2041 }
2042 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2043 if (alpha > 0)
2044 {
2045 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2046 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2047 }
2048 else
2049 {
2050 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2051 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2052 }
2053 PATH_AddEntry(pOutsidePath, &pt, PT_LINETO);
2054 break;
2055 case PS_JOIN_ROUND :
2056 default :
2057 if (alpha > 0)
2058 {
2059 pt.x = xo + round(penWidthOut * cos(theta + M_PI_2));
2060 pt.y = yo + round(penWidthOut * sin(theta + M_PI_2));
2061 }
2062 else
2063 {
2064 pt.x = xo - round(penWidthOut * cos(theta + M_PI_2));
2065 pt.y = yo - round(penWidthOut * sin(theta + M_PI_2));
2066 }
2067 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2068 pt.x = xo + round(penWidthOut * cos(theta + alpha / 2));
2069 pt.y = yo + round(penWidthOut * sin(theta + alpha / 2));
2070 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2071 if (alpha > 0)
2072 {
2073 pt.x = xo - round(penWidthOut * cos(M_PI_2 + alpha + theta));
2074 pt.y = yo - round(penWidthOut * sin(M_PI_2 + alpha + theta));
2075 }
2076 else
2077 {
2078 pt.x = xo + round(penWidthOut * cos(M_PI_2 + alpha + theta));
2079 pt.y = yo + round(penWidthOut * sin(M_PI_2 + alpha + theta));
2080 }
2081 PATH_AddEntry(pOutsidePath, &pt, PT_BEZIERTO);
2082 break;
2083 }
2084 }
2085 }
2086 type = add_points( pNewPath, pUpPath->pPoints, pUpPath->numEntriesUsed, PT_LINETO );
2087 type[0] = PT_MOVETO;
2088 reverse_points( pDownPath->pPoints, pDownPath->numEntriesUsed );
2089 type = add_points( pNewPath, pDownPath->pPoints, pDownPath->numEntriesUsed, PT_LINETO );
2090 if (pStrokes[i]->pFlags[pStrokes[i]->numEntriesUsed - 1] & PT_CLOSEFIGURE) type[0] = PT_MOVETO;
2091
2092 PATH_DestroyGdiPath(pStrokes[i]);
2093 ExFreePoolWithTag(pStrokes[i], TAG_PATH);
2094 PATH_DestroyGdiPath(pUpPath);
2095 ExFreePoolWithTag(pUpPath, TAG_PATH);
2096 PATH_DestroyGdiPath(pDownPath);
2097 ExFreePoolWithTag(pDownPath, TAG_PATH);
2098 }
2099 if (pStrokes) ExFreePoolWithTag(pStrokes, TAG_PATH);
2100
2101 PATH_UnlockPath(flat_path);
2102 PATH_Delete(flat_path->BaseObject.hHmgr);
2103 pNewPath->state = PATH_Closed;
2104 PATH_UnlockPath(pNewPath);
2105 return pNewPath;
2106 }
2107
2108 static inline INT int_from_fixed(FIXED f)
2109 {
2110 return (f.fract >= 0x8000) ? (f.value + 1) : f.value;
2111 }
2112
2113 /**********************************************************************
2114 * PATH_BezierTo
2115 *
2116 * Internally used by PATH_add_outline
2117 */
2118 static
2119 VOID
2120 FASTCALL
2121 PATH_BezierTo(
2122 PPATH pPath,
2123 POINT *lppt,
2124 INT n)
2125 {
2126 if (n < 2) return;
2127
2128 if (n == 2)
2129 {
2130 PATH_AddEntry(pPath, &lppt[1], PT_LINETO);
2131 }
2132 else if (n == 3)
2133 {
2134 add_points( pPath, lppt, 3, PT_BEZIERTO );
2135 }
2136 else
2137 {
2138 POINT pt[3];
2139 INT i = 0;
2140
2141 pt[2] = lppt[0];
2142 n--;
2143
2144 while (n > 2)
2145 {
2146 pt[0] = pt[2];
2147 pt[1] = lppt[i + 1];
2148 pt[2].x = (lppt[i + 2].x + lppt[i + 1].x) / 2;
2149 pt[2].y = (lppt[i + 2].y + lppt[i + 1].y) / 2;
2150 add_points( pPath, pt, 3, PT_BEZIERTO );
2151 n--;
2152 i++;
2153 }
2154
2155 pt[0] = pt[2];
2156 pt[1] = lppt[i + 1];
2157 pt[2] = lppt[i + 2];
2158 add_points( pPath, pt, 3, PT_BEZIERTO );
2159 }
2160 }
2161
2162 static
2163 BOOL
2164 FASTCALL
2165 PATH_add_outline(
2166 PDC dc,
2167 PPATH pPath,
2168 INT x,
2169 INT y,
2170 TTPOLYGONHEADER *header,
2171 DWORD size)
2172 {
2173 TTPOLYGONHEADER *start;
2174 POINT pt;
2175 BOOL bResult = FALSE;
2176
2177 start = header;
2178
2179 while ((char *)header < (char *)start + size)
2180 {
2181 TTPOLYCURVE *curve;
2182
2183 if (header->dwType != TT_POLYGON_TYPE)
2184 {
2185 DPRINT1("Unknown header type %lu\n", header->dwType);
2186 goto cleanup;
2187 }
2188
2189 pt.x = x + int_from_fixed(header->pfxStart.x);
2190 pt.y = y - int_from_fixed(header->pfxStart.y);
2191 PATH_AddEntry(pPath, &pt, PT_MOVETO);
2192
2193 curve = (TTPOLYCURVE *)(header + 1);
2194
2195 while ((char *)curve < (char *)header + header->cb)
2196 {
2197 /*DPRINT1("curve->wType %d\n", curve->wType);*/
2198
2199 switch(curve->wType)
2200 {
2201 case TT_PRIM_LINE:
2202 {
2203 WORD i;
2204
2205 for (i = 0; i < curve->cpfx; i++)
2206 {
2207 pt.x = x + int_from_fixed(curve->apfx[i].x);
2208 pt.y = y - int_from_fixed(curve->apfx[i].y);
2209 PATH_AddEntry(pPath, &pt, PT_LINETO);
2210 }
2211 break;
2212 }
2213
2214 case TT_PRIM_QSPLINE:
2215 case TT_PRIM_CSPLINE:
2216 {
2217 WORD i;
2218 POINTFX ptfx;
2219 POINT *pts = ExAllocatePoolWithTag(PagedPool, (curve->cpfx + 1) * sizeof(POINT), TAG_PATH);
2220
2221 if (!pts) goto cleanup;
2222
2223 ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
2224
2225 pts[0].x = x + int_from_fixed(ptfx.x);
2226 pts[0].y = y - int_from_fixed(ptfx.y);
2227
2228 for (i = 0; i < curve->cpfx; i++)
2229 {
2230 pts[i + 1].x = x + int_from_fixed(curve->apfx[i].x);
2231 pts[i + 1].y = y - int_from_fixed(curve->apfx[i].y);
2232 }
2233
2234 PATH_BezierTo(pPath, pts, curve->cpfx + 1);
2235
2236 ExFreePoolWithTag(pts, TAG_PATH);
2237 break;
2238 }
2239
2240 default:
2241 DPRINT1("Unknown curve type %04x\n", curve->wType);
2242 goto cleanup;
2243 }
2244
2245 curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
2246 }
2247 header = (TTPOLYGONHEADER *)((char *)header + header->cb);
2248 }
2249
2250 bResult = TRUE;
2251
2252 cleanup:
2253 IntGdiCloseFigure(pPath);
2254 return bResult;
2255 }
2256
2257 /**********************************************************************
2258 * PATH_ExtTextOut
2259 */
2260 BOOL
2261 FASTCALL
2262 PATH_ExtTextOut(
2263 PDC dc,
2264 INT x,
2265 INT y,
2266 UINT flags,
2267 const RECTL *lprc,
2268 LPCWSTR str,
2269 UINT count,
2270 const INT *dx)
2271 {
2272 PPATH pPath;
2273 unsigned int idx, ggo_flags = GGO_NATIVE;
2274 POINT offset = {0, 0};
2275
2276 pPath = PATH_LockPath(dc->dclevel.hPath);
2277 if (!pPath)
2278 {
2279 return FALSE;
2280 }
2281
2282 if (pPath->state != PATH_Open)
2283 {
2284 DPRINT1("PATH_ExtTextOut not open\n");
2285 return FALSE;
2286 }
2287
2288 if (!count) return TRUE;
2289 if (flags & ETO_GLYPH_INDEX) ggo_flags |= GGO_GLYPH_INDEX;
2290
2291 for (idx = 0; idx < count; idx++)
2292 {
2293 MAT2 identity = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
2294 GLYPHMETRICS gm;
2295 DWORD dwSize;
2296 void *outline;
2297
2298 dwSize = ftGdiGetGlyphOutline(dc,
2299 str[idx],
2300 ggo_flags,
2301 &gm,
2302 0,
2303 NULL,
2304 &identity,
2305 TRUE);
2306 if (dwSize == GDI_ERROR)
2307 {
2308 PATH_UnlockPath(pPath);
2309 return FALSE;
2310 }
2311
2312 /* Add outline only if char is printable */
2313 if (dwSize)
2314 {
2315 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH);
2316 if (!outline)
2317 {
2318 PATH_UnlockPath(pPath);
2319 return FALSE;
2320 }
2321
2322 ftGdiGetGlyphOutline(dc,
2323 str[idx],
2324 ggo_flags,
2325 &gm,
2326 dwSize,
2327 outline,
2328 &identity,
2329 TRUE);
2330
2331 PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize);
2332
2333 ExFreePoolWithTag(outline, TAG_PATH);
2334 }
2335
2336 if (dx)
2337 {
2338 if (flags & ETO_PDY)
2339 {
2340 offset.x += dx[idx * 2];
2341 offset.y += dx[idx * 2 + 1];
2342 }
2343 else
2344 offset.x += dx[idx];
2345 }
2346 else
2347 {
2348 offset.x += gm.gmCellIncX;
2349 offset.y += gm.gmCellIncY;
2350 }
2351 }
2352 PATH_UnlockPath(pPath);
2353 return TRUE;
2354 }
2355
2356
2357 /***********************************************************************
2358 * Exported functions
2359 */
2360
2361 BOOL
2362 APIENTRY
2363 NtGdiAbortPath(HDC hDC)
2364 {
2365 PDC dc = DC_LockDc(hDC);
2366 if (!dc)
2367 {
2368 EngSetLastError(ERROR_INVALID_HANDLE);
2369 return FALSE;
2370 }
2371
2372 if (!dc->dclevel.hPath)
2373 {
2374 DC_UnlockDc(dc);
2375 return TRUE;
2376 }
2377
2378 if (!PATH_Delete(dc->dclevel.hPath))
2379 {
2380 DC_UnlockDc(dc);
2381 return FALSE;
2382 }
2383
2384 dc->dclevel.hPath = 0;
2385 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2386
2387 DC_UnlockDc(dc);
2388 return TRUE;
2389 }
2390
2391 BOOL
2392 APIENTRY
2393 NtGdiBeginPath(HDC hDC)
2394 {
2395 PPATH pPath;
2396 PDC dc;
2397
2398 dc = DC_LockDc(hDC);
2399 if (!dc)
2400 {
2401 EngSetLastError(ERROR_INVALID_HANDLE);
2402 return FALSE;
2403 }
2404
2405 /* If path is already open, do nothing. Check if not Save DC state */
2406 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE))
2407 {
2408 DC_UnlockDc(dc);
2409 return TRUE;
2410 }
2411
2412 if (dc->dclevel.hPath)
2413 {
2414 DPRINT("BeginPath 1 0x%p\n", dc->dclevel.hPath);
2415 if (!(dc->dclevel.flPath & DCPATH_SAVE))
2416 {
2417 // Remove previous handle.
2418 if (!PATH_Delete(dc->dclevel.hPath))
2419 {
2420 DC_UnlockDc(dc);
2421 return FALSE;
2422 }
2423 }
2424 else
2425 {
2426 // Clear flags and Handle.
2427 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE);
2428 dc->dclevel.hPath = NULL;
2429 }
2430 }
2431 pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL);
2432 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP!
2433 dc->dclevel.hPath = pPath->BaseObject.hHmgr;
2434 IntGetCurrentPositionEx(dc, &pPath->pos);
2435 IntLPtoDP( dc, &pPath->pos, 1 );
2436 DPRINT("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y);
2437 PATH_UnlockPath(pPath);
2438 DC_UnlockDc(dc);
2439
2440 if (!pPath)
2441 {
2442 return FALSE;
2443 }
2444 return TRUE;
2445 }
2446
2447 BOOL
2448 APIENTRY
2449 NtGdiCloseFigure(HDC hDC)
2450 {
2451 BOOL Ret = FALSE; // Default to failure
2452 PDC pDc;
2453 PPATH pPath;
2454
2455 DPRINT("Enter %s\n", __FUNCTION__);
2456
2457 pDc = DC_LockDc(hDC);
2458 if (!pDc)
2459 {
2460 EngSetLastError(ERROR_INVALID_PARAMETER);
2461 return FALSE;
2462 }
2463
2464 pPath = PATH_LockPath(pDc->dclevel.hPath);
2465 if (!pPath)
2466 {
2467 DC_UnlockDc(pDc);
2468 return FALSE;
2469 }
2470
2471 if (pPath->state == PATH_Open)
2472 {
2473 IntGdiCloseFigure(pPath);
2474 Ret = TRUE;
2475 }
2476 else
2477 {
2478 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2479 }
2480
2481 PATH_UnlockPath(pPath);
2482 DC_UnlockDc(pDc);
2483 return Ret;
2484 }
2485
2486 BOOL
2487 APIENTRY
2488 NtGdiEndPath(HDC hDC)
2489 {
2490 BOOL ret = TRUE;
2491 PPATH pPath;
2492 PDC dc;
2493
2494 dc = DC_LockDc(hDC);
2495 if (!dc)
2496 {
2497 EngSetLastError(ERROR_INVALID_HANDLE);
2498 return FALSE;
2499 }
2500
2501 pPath = PATH_LockPath(dc->dclevel.hPath);
2502 if (!pPath)
2503 {
2504 DC_UnlockDc(dc);
2505 return FALSE;
2506 }
2507
2508 /* Check that path is currently being constructed */
2509 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE))
2510 {
2511 DPRINT("EndPath ERROR! 0x%p\n", dc->dclevel.hPath);
2512 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2513 ret = FALSE;
2514 }
2515 /* Set flag to indicate that path is finished */
2516 else
2517 {
2518 DPRINT("EndPath 0x%p\n", dc->dclevel.hPath);
2519 pPath->state = PATH_Closed;
2520 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2521 }
2522
2523 PATH_UnlockPath(pPath);
2524 DC_UnlockDc(dc);
2525 return ret;
2526 }
2527
2528 BOOL
2529 APIENTRY
2530 NtGdiFillPath(HDC hDC)
2531 {
2532 BOOL ret = FALSE;
2533 PPATH pPath, pNewPath;
2534 PDC_ATTR pdcattr;
2535 PDC dc;
2536
2537 dc = DC_LockDc(hDC);
2538 if (!dc)
2539 {
2540 EngSetLastError(ERROR_INVALID_PARAMETER);
2541 return FALSE;
2542 }
2543
2544 pPath = PATH_LockPath(dc->dclevel.hPath);
2545 if (!pPath)
2546 {
2547 DC_UnlockDc(dc);
2548 return FALSE;
2549 }
2550
2551 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL);
2552
2553 pdcattr = dc->pdcattr;
2554
2555 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2556 DC_vUpdateLineBrush(dc);
2557
2558 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2559 DC_vUpdateFillBrush(dc);
2560
2561 pNewPath = PATH_FlattenPath(pPath);
2562
2563 if (pNewPath->state != PATH_Closed)
2564 {
2565 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2566 }
2567 else if (pNewPath->numEntriesUsed)
2568 {
2569 ret = PATH_FillPath(dc, pNewPath);
2570 }
2571 else ret = TRUE;
2572
2573 PATH_UnlockPath(pNewPath);
2574 PATH_Delete(pNewPath->BaseObject.hHmgr);
2575
2576 PATH_UnlockPath(pPath);
2577 PATH_Delete(pPath->BaseObject.hHmgr);
2578 dc->dclevel.hPath = 0;
2579 dc->dclevel.flPath &= ~DCPATH_ACTIVE;
2580
2581 DC_vFinishBlit(dc, NULL);
2582 DC_UnlockDc(dc);
2583 return ret;
2584 }
2585
2586 BOOL
2587 APIENTRY
2588 NtGdiFlattenPath(HDC hDC)
2589 {
2590 BOOL Ret = FALSE;
2591 DC *pDc;
2592 PPATH pPath, pNewPath = NULL;
2593
2594 DPRINT("Enter %s\n", __FUNCTION__);
2595
2596 pDc = DC_LockDc(hDC);
2597 if (!pDc)
2598 {
2599 EngSetLastError(ERROR_INVALID_HANDLE);
2600 return FALSE;
2601 }
2602
2603 pPath = PATH_LockPath(pDc->dclevel.hPath);
2604 if (!pPath)
2605 {
2606 EngSetLastError( ERROR_CAN_NOT_COMPLETE );
2607 DC_UnlockDc(pDc);
2608 return FALSE;
2609 }
2610
2611 if (pPath->state == PATH_Closed)
2612 {
2613 pNewPath = PATH_FlattenPath(pPath);
2614 }
2615
2616 PATH_UnlockPath(pPath);
2617
2618 if (pNewPath)
2619 {
2620 PATH_Delete(pDc->dclevel.hPath);
2621 pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr;
2622 PATH_UnlockPath(pNewPath);
2623 Ret = TRUE;
2624 }
2625
2626 DC_UnlockDc(pDc);
2627 return Ret;
2628 }
2629
2630 _Success_(return != FALSE)
2631 BOOL
2632 APIENTRY
2633 NtGdiGetMiterLimit(
2634 _In_ HDC hdc,
2635 _Out_ PDWORD pdwOut)
2636 {
2637 DC *pDc;
2638 BOOL bResult = TRUE;
2639
2640 if (!(pDc = DC_LockDc(hdc)))
2641 {
2642 EngSetLastError(ERROR_INVALID_PARAMETER);
2643 return FALSE;
2644 }
2645
2646 _SEH2_TRY
2647 {
2648 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2649 *pdwOut = pDc->dclevel.laPath.eMiterLimit;
2650 }
2651 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2652 {
2653 SetLastNtError(_SEH2_GetExceptionCode());
2654 bResult = FALSE;
2655 }
2656 _SEH2_END;
2657
2658 DC_UnlockDc(pDc);
2659 return bResult;
2660
2661 }
2662
2663 INT
2664 APIENTRY
2665 NtGdiGetPath(
2666 HDC hDC,
2667 LPPOINT Points,
2668 LPBYTE Types,
2669 INT nSize)
2670 {
2671 INT ret = -1;
2672 PPATH pPath;
2673
2674 DC *dc = DC_LockDc(hDC);
2675 DPRINT("NtGdiGetPath start\n");
2676 if (!dc)
2677 {
2678 DPRINT1("Can't lock dc!\n");
2679 EngSetLastError(ERROR_INVALID_PARAMETER);
2680 return -1;
2681 }
2682
2683 pPath = PATH_LockPath(dc->dclevel.hPath);
2684 if (!pPath)
2685 {
2686 DC_UnlockDc(dc);
2687 return -1;
2688 }
2689
2690 if (pPath->state != PATH_Closed)
2691 {
2692 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2693 goto done;
2694 }
2695
2696 if (nSize == 0)
2697 {
2698 ret = pPath->numEntriesUsed;
2699 }
2700 else if (nSize < pPath->numEntriesUsed)
2701 {
2702 EngSetLastError(ERROR_INVALID_PARAMETER);
2703 goto done;
2704 }
2705 else
2706 {
2707 _SEH2_TRY
2708 {
2709 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed);
2710 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed);
2711
2712 /* Convert the points to logical coordinates */
2713 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed))
2714 {
2715 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW);
2716 _SEH2_LEAVE;
2717 }
2718
2719 ret = pPath->numEntriesUsed;
2720 }
2721 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2722 {
2723 SetLastNtError(_SEH2_GetExceptionCode());
2724 }
2725 _SEH2_END
2726 }
2727
2728 done:
2729 DPRINT("NtGdiGetPath exit %d\n",ret);
2730 PATH_UnlockPath(pPath);
2731 DC_UnlockDc(dc);
2732 return ret;
2733 }
2734
2735 HRGN
2736 APIENTRY
2737 NtGdiPathToRegion(HDC hDC)
2738 {
2739 PPATH pPath, pNewPath;
2740 HRGN hrgnRval = 0;
2741 int Ret;
2742 PREGION Rgn;
2743 DC *pDc;
2744 PDC_ATTR pdcattr;
2745
2746 DPRINT("Enter %s\n", __FUNCTION__);
2747
2748 pDc = DC_LockDc(hDC);
2749 if (!pDc)
2750 {
2751 DPRINT("Failed to lock DC %p\n", hDC);
2752 EngSetLastError(ERROR_INVALID_PARAMETER);
2753 return NULL;
2754 }
2755
2756 pdcattr = pDc->pdcattr;
2757
2758 pPath = PATH_LockPath(pDc->dclevel.hPath);
2759 if (!pPath)
2760 {
2761 DPRINT("Failed to lock DC path %p\n", pDc->dclevel.hPath);
2762 DC_UnlockDc(pDc);
2763 return NULL;
2764 }
2765
2766 if (pPath->state != PATH_Closed)
2767 {
2768 // FIXME: Check that setlasterror is being called correctly
2769 DPRINT("Path is not closed!\n");
2770 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2771 }
2772 else
2773 {
2774 /* Create the region and fill it with the path strokes */
2775 Rgn = REGION_AllocUserRgnWithHandle(1);
2776 if (!Rgn)
2777 {
2778 DPRINT("Failed to allocate a region\n");
2779 PATH_UnlockPath(pPath);
2780 DC_UnlockDc(pDc);
2781 return NULL;
2782 }
2783 hrgnRval = Rgn->BaseObject.hHmgr;
2784
2785 pNewPath = PATH_FlattenPath(pPath);
2786
2787 Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn);
2788
2789 PATH_UnlockPath(pNewPath);
2790 PATH_Delete(pNewPath->BaseObject.hHmgr);
2791
2792 if (!Ret)
2793 {
2794 DPRINT("PATH_PathToRegion failed\n");
2795 REGION_Delete(Rgn);
2796 hrgnRval = NULL;
2797 }
2798 else
2799 REGION_UnlockRgn(Rgn);
2800 }
2801
2802 PATH_UnlockPath(pPath);
2803 PATH_Delete(pDc->dclevel.hPath);
2804 pDc->dclevel.hPath = NULL;
2805 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2806
2807 DC_UnlockDc(pDc);
2808 return hrgnRval;
2809 }
2810
2811 BOOL
2812 APIENTRY
2813 NtGdiSetMiterLimit(
2814 IN HDC hdc,
2815 IN DWORD dwNew,
2816 IN OUT OPTIONAL PDWORD pdwOut)
2817 {
2818 DC *pDc;
2819 gxf_long worker, worker1;
2820 BOOL bResult = TRUE;
2821
2822 if (!(pDc = DC_LockDc(hdc)))
2823 {
2824 EngSetLastError(ERROR_INVALID_PARAMETER);
2825 return FALSE;
2826 }
2827
2828 worker.l = dwNew;
2829 worker1.f = pDc->dclevel.laPath.eMiterLimit;
2830 pDc->dclevel.laPath.eMiterLimit = worker.f;
2831
2832 if (pdwOut)
2833 {
2834 _SEH2_TRY
2835 {
2836 ProbeForWrite(pdwOut, sizeof(DWORD), 1);
2837 *pdwOut = worker1.l;
2838 }
2839 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2840 {
2841 SetLastNtError(_SEH2_GetExceptionCode());
2842 bResult = FALSE;
2843 }
2844 _SEH2_END;
2845 }
2846
2847 DC_UnlockDc(pDc);
2848 return bResult;
2849 }
2850
2851 BOOL
2852 APIENTRY
2853 NtGdiStrokeAndFillPath(HDC hDC)
2854 {
2855 DC *pDc;
2856 PDC_ATTR pdcattr;
2857 PPATH pPath, pNewPath;
2858 BOOL bRet = FALSE;
2859
2860 DPRINT("Enter %s\n", __FUNCTION__);
2861
2862 if (!(pDc = DC_LockDc(hDC)))
2863 {
2864 EngSetLastError(ERROR_INVALID_PARAMETER);
2865 return FALSE;
2866 }
2867 pPath = PATH_LockPath(pDc->dclevel.hPath);
2868 if (!pPath)
2869 {
2870 DC_UnlockDc(pDc);
2871 return FALSE;
2872 }
2873
2874 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2875
2876 pdcattr = pDc->pdcattr;
2877
2878 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY))
2879 DC_vUpdateFillBrush(pDc);
2880
2881 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2882 DC_vUpdateLineBrush(pDc);
2883
2884 pNewPath = PATH_FlattenPath(pPath);
2885
2886 if (pNewPath->state != PATH_Closed)
2887 {
2888 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2889 }
2890 else if (pNewPath->numEntriesUsed)
2891 {
2892 bRet = PATH_FillPath(pDc, pNewPath);
2893 if (bRet) bRet = PATH_StrokePath(pDc, pNewPath);
2894 }
2895 else bRet = TRUE;
2896
2897 PATH_UnlockPath(pNewPath);
2898 PATH_Delete(pNewPath->BaseObject.hHmgr);
2899
2900 PATH_UnlockPath(pPath);
2901 PATH_Delete(pPath->BaseObject.hHmgr);
2902 pDc->dclevel.hPath = 0;
2903 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2904
2905 DC_vFinishBlit(pDc, NULL);
2906 DC_UnlockDc(pDc);
2907 return bRet;
2908 }
2909
2910 BOOL
2911 APIENTRY
2912 NtGdiStrokePath(HDC hDC)
2913 {
2914 DC *pDc;
2915 PDC_ATTR pdcattr;
2916 PPATH pPath, pNewPath;
2917 BOOL bRet = FALSE;
2918
2919 DPRINT("Enter %s\n", __FUNCTION__);
2920
2921 if (!(pDc = DC_LockDc(hDC)))
2922 {
2923 EngSetLastError(ERROR_INVALID_PARAMETER);
2924 return FALSE;
2925 }
2926
2927 pPath = PATH_LockPath(pDc->dclevel.hPath);
2928 if (!pPath)
2929 {
2930 DC_UnlockDc(pDc);
2931 return FALSE;
2932 }
2933
2934 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL);
2935
2936 pdcattr = pDc->pdcattr;
2937
2938 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY))
2939 DC_vUpdateLineBrush(pDc);
2940
2941 pNewPath = PATH_FlattenPath(pPath);
2942
2943 if (pNewPath->state != PATH_Closed)
2944 {
2945 EngSetLastError(ERROR_CAN_NOT_COMPLETE);
2946 }
2947 else bRet = PATH_StrokePath(pDc, pNewPath);
2948
2949 PATH_UnlockPath(pNewPath);
2950 PATH_Delete(pNewPath->BaseObject.hHmgr);
2951
2952 DC_vFinishBlit(pDc, NULL);
2953
2954 PATH_UnlockPath(pPath);
2955 PATH_Delete(pPath->BaseObject.hHmgr);
2956 pDc->dclevel.hPath = 0;
2957 pDc->dclevel.flPath &= ~DCPATH_ACTIVE;
2958
2959 DC_UnlockDc(pDc);
2960 return bRet;
2961 }
2962
2963 BOOL
2964 APIENTRY
2965 NtGdiWidenPath(HDC hDC)
2966 {
2967 PPATH pPath;
2968 BOOL Ret = FALSE;
2969 PDC pdc = DC_LockDc(hDC);
2970 DPRINT("NtGdiWidenPat Enter\n");
2971 if (!pdc)
2972 {
2973 EngSetLastError(ERROR_INVALID_PARAMETER);
2974 return FALSE;
2975 }
2976
2977 pPath = PATH_WidenPath(pdc);
2978 if (pPath)
2979 {
2980 DPRINT("WindenPath New Path\n");
2981 PATH_Delete(pdc->dclevel.hPath);
2982 pdc->dclevel.hPath = pPath->BaseObject.hHmgr;
2983 Ret = TRUE;
2984 }
2985 DC_UnlockDc(pdc);
2986 DPRINT("NtGdiWidenPat Ret %d\n",Ret);
2987 return Ret;
2988 }
2989
2990 /* EOF */