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