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