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