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 PATH_UnlockPath(pPath); 2382 return FALSE; 2383 } 2384 2385 /* Add outline only if char is printable */ 2386 if (dwSize) 2387 { 2388 outline = ExAllocatePoolWithTag(PagedPool, dwSize, TAG_PATH); 2389 if (!outline) 2390 { 2391 PATH_UnlockPath(pPath); 2392 return FALSE; 2393 } 2394 2395 ftGdiGetGlyphOutline(dc, 2396 str[idx], 2397 ggo_flags, 2398 &gm, 2399 dwSize, 2400 outline, 2401 &identity, 2402 TRUE); 2403 2404 PATH_add_outline(dc, pPath, x + offset.x, y + offset.y, outline, dwSize); 2405 2406 ExFreePoolWithTag(outline, TAG_PATH); 2407 } 2408 2409 if (dx) 2410 { 2411 if (flags & ETO_PDY) 2412 { 2413 offset.x += dx[idx * 2]; 2414 offset.y += dx[idx * 2 + 1]; 2415 } 2416 else 2417 offset.x += dx[idx]; 2418 } 2419 else 2420 { 2421 offset.x += gm.gmCellIncX; 2422 offset.y += gm.gmCellIncY; 2423 } 2424 } 2425 PATH_UnlockPath(pPath); 2426 return TRUE; 2427 } 2428 2429 2430 /*********************************************************************** 2431 * Exported functions 2432 */ 2433 2434 BOOL 2435 APIENTRY 2436 NtGdiAbortPath(HDC hDC) 2437 { 2438 PDC dc = DC_LockDc(hDC); 2439 if (!dc) 2440 { 2441 EngSetLastError(ERROR_INVALID_HANDLE); 2442 return FALSE; 2443 } 2444 2445 if (!dc->dclevel.hPath) 2446 { 2447 DC_UnlockDc(dc); 2448 return TRUE; 2449 } 2450 2451 if (!PATH_Delete(dc->dclevel.hPath)) 2452 { 2453 DC_UnlockDc(dc); 2454 return FALSE; 2455 } 2456 2457 dc->dclevel.hPath = 0; 2458 dc->dclevel.flPath &= ~DCPATH_ACTIVE; 2459 2460 DC_UnlockDc(dc); 2461 return TRUE; 2462 } 2463 2464 BOOL 2465 APIENTRY 2466 NtGdiBeginPath(HDC hDC) 2467 { 2468 PPATH pPath; 2469 PDC dc; 2470 2471 dc = DC_LockDc(hDC); 2472 if (!dc) 2473 { 2474 EngSetLastError(ERROR_INVALID_HANDLE); 2475 return FALSE; 2476 } 2477 2478 /* If path is already open, do nothing. Check if not Save DC state */ 2479 if ((dc->dclevel.flPath & DCPATH_ACTIVE) && !(dc->dclevel.flPath & DCPATH_SAVE)) 2480 { 2481 DC_UnlockDc(dc); 2482 return TRUE; 2483 } 2484 2485 if (dc->dclevel.hPath) 2486 { 2487 TRACE("BeginPath 1 0x%p\n", dc->dclevel.hPath); 2488 if (!(dc->dclevel.flPath & DCPATH_SAVE)) 2489 { 2490 // Remove previous handle. 2491 if (!PATH_Delete(dc->dclevel.hPath)) 2492 { 2493 DC_UnlockDc(dc); 2494 return FALSE; 2495 } 2496 } 2497 else 2498 { 2499 // Clear flags and Handle. 2500 dc->dclevel.flPath &= ~(DCPATH_SAVE | DCPATH_ACTIVE); 2501 dc->dclevel.hPath = NULL; 2502 } 2503 } 2504 pPath = PATH_CreatePath(NUM_ENTRIES_INITIAL); 2505 dc->dclevel.flPath |= DCPATH_ACTIVE; // Set active ASAP! 2506 dc->dclevel.hPath = pPath->BaseObject.hHmgr; 2507 IntGetCurrentPositionEx(dc, &pPath->pos); 2508 IntLPtoDP( dc, &pPath->pos, 1 ); 2509 TRACE("BP : Current pos X %d Y %d\n",pPath->pos.x, pPath->pos.y); 2510 PATH_UnlockPath(pPath); 2511 DC_UnlockDc(dc); 2512 2513 if (!pPath) 2514 { 2515 return FALSE; 2516 } 2517 return TRUE; 2518 } 2519 2520 BOOL 2521 APIENTRY 2522 NtGdiCloseFigure(HDC hDC) 2523 { 2524 BOOL Ret = FALSE; // Default to failure 2525 PDC pDc; 2526 PPATH pPath; 2527 2528 TRACE("Enter %s\n", __FUNCTION__); 2529 2530 pDc = DC_LockDc(hDC); 2531 if (!pDc) 2532 { 2533 EngSetLastError(ERROR_INVALID_PARAMETER); 2534 return FALSE; 2535 } 2536 2537 pPath = PATH_LockPath(pDc->dclevel.hPath); 2538 if (!pPath) 2539 { 2540 DC_UnlockDc(pDc); 2541 return FALSE; 2542 } 2543 2544 if (pPath->state == PATH_Open) 2545 { 2546 IntGdiCloseFigure(pPath); 2547 Ret = TRUE; 2548 } 2549 else 2550 { 2551 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2552 } 2553 2554 PATH_UnlockPath(pPath); 2555 DC_UnlockDc(pDc); 2556 return Ret; 2557 } 2558 2559 BOOL 2560 APIENTRY 2561 NtGdiEndPath(HDC hDC) 2562 { 2563 BOOL ret = TRUE; 2564 PPATH pPath; 2565 PDC dc; 2566 2567 dc = DC_LockDc(hDC); 2568 if (!dc) 2569 { 2570 EngSetLastError(ERROR_INVALID_HANDLE); 2571 return FALSE; 2572 } 2573 2574 pPath = PATH_LockPath(dc->dclevel.hPath); 2575 if (!pPath) 2576 { 2577 DC_UnlockDc(dc); 2578 return FALSE; 2579 } 2580 2581 /* Check that path is currently being constructed */ 2582 if ((pPath->state != PATH_Open) || !(dc->dclevel.flPath & DCPATH_ACTIVE)) 2583 { 2584 TRACE("EndPath ERROR! 0x%p\n", dc->dclevel.hPath); 2585 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2586 ret = FALSE; 2587 } 2588 /* Set flag to indicate that path is finished */ 2589 else 2590 { 2591 TRACE("EndPath 0x%p\n", dc->dclevel.hPath); 2592 pPath->state = PATH_Closed; 2593 dc->dclevel.flPath &= ~DCPATH_ACTIVE; 2594 } 2595 2596 PATH_UnlockPath(pPath); 2597 DC_UnlockDc(dc); 2598 return ret; 2599 } 2600 2601 BOOL 2602 APIENTRY 2603 NtGdiFillPath(HDC hDC) 2604 { 2605 BOOL ret = FALSE; 2606 PPATH pPath, pNewPath; 2607 PDC_ATTR pdcattr; 2608 PDC dc; 2609 2610 dc = DC_LockDc(hDC); 2611 if (!dc) 2612 { 2613 EngSetLastError(ERROR_INVALID_PARAMETER); 2614 return FALSE; 2615 } 2616 2617 pPath = PATH_LockPath(dc->dclevel.hPath); 2618 if (!pPath) 2619 { 2620 DC_UnlockDc(dc); 2621 return FALSE; 2622 } 2623 2624 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL); 2625 2626 pdcattr = dc->pdcattr; 2627 2628 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY)) 2629 DC_vUpdateLineBrush(dc); 2630 2631 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)) 2632 DC_vUpdateFillBrush(dc); 2633 2634 pNewPath = PATH_FlattenPath(pPath); 2635 2636 if (pNewPath->state != PATH_Closed) 2637 { 2638 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2639 } 2640 else if (pNewPath->numEntriesUsed) 2641 { 2642 ret = PATH_FillPath(dc, pNewPath); 2643 } 2644 else ret = TRUE; 2645 2646 PATH_UnlockPath(pNewPath); 2647 PATH_Delete(pNewPath->BaseObject.hHmgr); 2648 2649 PATH_UnlockPath(pPath); 2650 PATH_Delete(pPath->BaseObject.hHmgr); 2651 dc->dclevel.hPath = 0; 2652 dc->dclevel.flPath &= ~DCPATH_ACTIVE; 2653 2654 DC_vFinishBlit(dc, NULL); 2655 DC_UnlockDc(dc); 2656 return ret; 2657 } 2658 2659 BOOL 2660 APIENTRY 2661 NtGdiFlattenPath(HDC hDC) 2662 { 2663 BOOL Ret = FALSE; 2664 DC *pDc; 2665 PPATH pPath, pNewPath = NULL; 2666 2667 TRACE("Enter %s\n", __FUNCTION__); 2668 2669 pDc = DC_LockDc(hDC); 2670 if (!pDc) 2671 { 2672 EngSetLastError(ERROR_INVALID_HANDLE); 2673 return FALSE; 2674 } 2675 2676 pPath = PATH_LockPath(pDc->dclevel.hPath); 2677 if (!pPath) 2678 { 2679 EngSetLastError( ERROR_CAN_NOT_COMPLETE ); 2680 DC_UnlockDc(pDc); 2681 return FALSE; 2682 } 2683 2684 if (pPath->state == PATH_Closed) 2685 { 2686 pNewPath = PATH_FlattenPath(pPath); 2687 } 2688 2689 PATH_UnlockPath(pPath); 2690 2691 if (pNewPath) 2692 { 2693 PATH_Delete(pDc->dclevel.hPath); 2694 pDc->dclevel.hPath = pNewPath->BaseObject.hHmgr; 2695 PATH_UnlockPath(pNewPath); 2696 Ret = TRUE; 2697 } 2698 2699 DC_UnlockDc(pDc); 2700 return Ret; 2701 } 2702 2703 _Success_(return != FALSE) 2704 BOOL 2705 APIENTRY 2706 NtGdiGetMiterLimit( 2707 _In_ HDC hdc, 2708 _Out_ PDWORD pdwOut) 2709 { 2710 DC *pDc; 2711 BOOL bResult = TRUE; 2712 2713 if (!(pDc = DC_LockDc(hdc))) 2714 { 2715 EngSetLastError(ERROR_INVALID_PARAMETER); 2716 return FALSE; 2717 } 2718 2719 _SEH2_TRY 2720 { 2721 ProbeForWrite(pdwOut, sizeof(DWORD), 1); 2722 *pdwOut = pDc->dclevel.laPath.eMiterLimit; 2723 } 2724 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2725 { 2726 SetLastNtError(_SEH2_GetExceptionCode()); 2727 bResult = FALSE; 2728 } 2729 _SEH2_END; 2730 2731 DC_UnlockDc(pDc); 2732 return bResult; 2733 2734 } 2735 2736 INT 2737 APIENTRY 2738 NtGdiGetPath( 2739 HDC hDC, 2740 LPPOINT Points, 2741 LPBYTE Types, 2742 INT nSize) 2743 { 2744 INT ret = -1; 2745 PPATH pPath; 2746 DC *dc; 2747 2748 _SEH2_TRY 2749 { 2750 ProbeForWrite(Points, nSize * sizeof(*Points), sizeof(ULONG)); 2751 ProbeForWrite(Types, nSize, 1); 2752 } 2753 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2754 { 2755 SetLastNtError(_SEH2_GetExceptionCode()); 2756 _SEH2_YIELD(return -1); 2757 } 2758 _SEH2_END 2759 2760 dc = DC_LockDc(hDC); 2761 TRACE("NtGdiGetPath start\n"); 2762 if (!dc) 2763 { 2764 ERR("Can't lock dc!\n"); 2765 EngSetLastError(ERROR_INVALID_PARAMETER); 2766 return -1; 2767 } 2768 2769 pPath = PATH_LockPath(dc->dclevel.hPath); 2770 if (!pPath) 2771 { 2772 DC_UnlockDc(dc); 2773 return -1; 2774 } 2775 2776 if (pPath->state != PATH_Closed) 2777 { 2778 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2779 goto done; 2780 } 2781 2782 if (nSize == 0) 2783 { 2784 ret = pPath->numEntriesUsed; 2785 } 2786 else if (nSize < pPath->numEntriesUsed) 2787 { 2788 EngSetLastError(ERROR_INVALID_PARAMETER); 2789 goto done; 2790 } 2791 else 2792 { 2793 _SEH2_TRY 2794 { 2795 memcpy(Points, pPath->pPoints, sizeof(POINT)*pPath->numEntriesUsed); 2796 memcpy(Types, pPath->pFlags, sizeof(BYTE)*pPath->numEntriesUsed); 2797 2798 /* Convert the points to logical coordinates */ 2799 if (!GdiPathDPtoLP(dc, Points, pPath->numEntriesUsed)) 2800 { 2801 EngSetLastError(ERROR_ARITHMETIC_OVERFLOW); 2802 _SEH2_LEAVE; 2803 } 2804 2805 ret = pPath->numEntriesUsed; 2806 } 2807 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2808 { 2809 SetLastNtError(_SEH2_GetExceptionCode()); 2810 } 2811 _SEH2_END 2812 } 2813 2814 done: 2815 TRACE("NtGdiGetPath exit %d\n",ret); 2816 PATH_UnlockPath(pPath); 2817 DC_UnlockDc(dc); 2818 return ret; 2819 } 2820 2821 HRGN 2822 APIENTRY 2823 NtGdiPathToRegion(HDC hDC) 2824 { 2825 PPATH pPath, pNewPath; 2826 HRGN hrgnRval = 0; 2827 int Ret; 2828 PREGION Rgn; 2829 DC *pDc; 2830 PDC_ATTR pdcattr; 2831 2832 TRACE("Enter %s\n", __FUNCTION__); 2833 2834 pDc = DC_LockDc(hDC); 2835 if (!pDc) 2836 { 2837 ERR("Failed to lock DC %p\n", hDC); 2838 EngSetLastError(ERROR_INVALID_PARAMETER); 2839 return NULL; 2840 } 2841 2842 pdcattr = pDc->pdcattr; 2843 2844 pPath = PATH_LockPath(pDc->dclevel.hPath); 2845 if (!pPath) 2846 { 2847 ERR("Failed to lock DC path %p\n", pDc->dclevel.hPath); 2848 DC_UnlockDc(pDc); 2849 return NULL; 2850 } 2851 2852 if (pPath->state != PATH_Closed) 2853 { 2854 // FIXME: Check that setlasterror is being called correctly 2855 ERR("Path is not closed!\n"); 2856 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2857 } 2858 else 2859 { 2860 /* Create the region and fill it with the path strokes */ 2861 Rgn = REGION_AllocUserRgnWithHandle(1); 2862 if (!Rgn) 2863 { 2864 ERR("Failed to allocate a region\n"); 2865 PATH_UnlockPath(pPath); 2866 DC_UnlockDc(pDc); 2867 return NULL; 2868 } 2869 hrgnRval = Rgn->BaseObject.hHmgr; 2870 2871 pNewPath = PATH_FlattenPath(pPath); 2872 2873 Ret = PATH_PathToRegion(pNewPath, pdcattr->jFillMode, Rgn); 2874 2875 PATH_UnlockPath(pNewPath); 2876 PATH_Delete(pNewPath->BaseObject.hHmgr); 2877 2878 if (!Ret) 2879 { 2880 ERR("PATH_PathToRegion failed\n"); 2881 REGION_Delete(Rgn); 2882 hrgnRval = NULL; 2883 } 2884 else 2885 REGION_UnlockRgn(Rgn); 2886 } 2887 2888 PATH_UnlockPath(pPath); 2889 PATH_Delete(pDc->dclevel.hPath); 2890 pDc->dclevel.hPath = NULL; 2891 pDc->dclevel.flPath &= ~DCPATH_ACTIVE; 2892 2893 DC_UnlockDc(pDc); 2894 return hrgnRval; 2895 } 2896 2897 BOOL 2898 APIENTRY 2899 NtGdiSetMiterLimit( 2900 IN HDC hdc, 2901 IN DWORD dwNew, 2902 IN OUT OPTIONAL PDWORD pdwOut) 2903 { 2904 DC *pDc; 2905 gxf_long worker, worker1; 2906 BOOL bResult = TRUE; 2907 2908 if (!(pDc = DC_LockDc(hdc))) 2909 { 2910 EngSetLastError(ERROR_INVALID_PARAMETER); 2911 return FALSE; 2912 } 2913 2914 worker.l = dwNew; 2915 worker1.f = pDc->dclevel.laPath.eMiterLimit; 2916 pDc->dclevel.laPath.eMiterLimit = worker.f; 2917 2918 if (pdwOut) 2919 { 2920 _SEH2_TRY 2921 { 2922 ProbeForWrite(pdwOut, sizeof(DWORD), 1); 2923 *pdwOut = worker1.l; 2924 } 2925 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2926 { 2927 SetLastNtError(_SEH2_GetExceptionCode()); 2928 bResult = FALSE; 2929 } 2930 _SEH2_END; 2931 } 2932 2933 DC_UnlockDc(pDc); 2934 return bResult; 2935 } 2936 2937 BOOL 2938 APIENTRY 2939 NtGdiStrokeAndFillPath(HDC hDC) 2940 { 2941 DC *pDc; 2942 PDC_ATTR pdcattr; 2943 PPATH pPath, pNewPath; 2944 BOOL bRet = FALSE; 2945 2946 TRACE("Enter %s\n", __FUNCTION__); 2947 2948 if (!(pDc = DC_LockDc(hDC))) 2949 { 2950 EngSetLastError(ERROR_INVALID_PARAMETER); 2951 return FALSE; 2952 } 2953 pPath = PATH_LockPath(pDc->dclevel.hPath); 2954 if (!pPath) 2955 { 2956 DC_UnlockDc(pDc); 2957 return FALSE; 2958 } 2959 2960 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL); 2961 2962 pdcattr = pDc->pdcattr; 2963 2964 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)) 2965 DC_vUpdateFillBrush(pDc); 2966 2967 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY)) 2968 DC_vUpdateLineBrush(pDc); 2969 2970 pNewPath = PATH_FlattenPath(pPath); 2971 2972 if (pNewPath->state != PATH_Closed) 2973 { 2974 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 2975 } 2976 else if (pNewPath->numEntriesUsed) 2977 { 2978 bRet = PATH_FillPath(pDc, pNewPath); 2979 if (bRet) bRet = PATH_StrokePath(pDc, pNewPath); 2980 } 2981 else bRet = TRUE; 2982 2983 PATH_UnlockPath(pNewPath); 2984 PATH_Delete(pNewPath->BaseObject.hHmgr); 2985 2986 PATH_UnlockPath(pPath); 2987 PATH_Delete(pPath->BaseObject.hHmgr); 2988 pDc->dclevel.hPath = 0; 2989 pDc->dclevel.flPath &= ~DCPATH_ACTIVE; 2990 2991 DC_vFinishBlit(pDc, NULL); 2992 DC_UnlockDc(pDc); 2993 return bRet; 2994 } 2995 2996 BOOL 2997 APIENTRY 2998 NtGdiStrokePath(HDC hDC) 2999 { 3000 DC *pDc; 3001 PDC_ATTR pdcattr; 3002 PPATH pPath, pNewPath; 3003 BOOL bRet = FALSE; 3004 3005 TRACE("Enter %s\n", __FUNCTION__); 3006 3007 if (!(pDc = DC_LockDc(hDC))) 3008 { 3009 EngSetLastError(ERROR_INVALID_PARAMETER); 3010 return FALSE; 3011 } 3012 3013 pPath = PATH_LockPath(pDc->dclevel.hPath); 3014 if (!pPath) 3015 { 3016 DC_UnlockDc(pDc); 3017 return FALSE; 3018 } 3019 3020 DC_vPrepareDCsForBlit(pDc, NULL, NULL, NULL); 3021 3022 pdcattr = pDc->pdcattr; 3023 3024 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY)) 3025 DC_vUpdateLineBrush(pDc); 3026 3027 pNewPath = PATH_FlattenPath(pPath); 3028 3029 if (pNewPath->state != PATH_Closed) 3030 { 3031 EngSetLastError(ERROR_CAN_NOT_COMPLETE); 3032 } 3033 else bRet = PATH_StrokePath(pDc, pNewPath); 3034 3035 PATH_UnlockPath(pNewPath); 3036 PATH_Delete(pNewPath->BaseObject.hHmgr); 3037 3038 DC_vFinishBlit(pDc, NULL); 3039 3040 PATH_UnlockPath(pPath); 3041 PATH_Delete(pPath->BaseObject.hHmgr); 3042 pDc->dclevel.hPath = 0; 3043 pDc->dclevel.flPath &= ~DCPATH_ACTIVE; 3044 3045 DC_UnlockDc(pDc); 3046 return bRet; 3047 } 3048 3049 BOOL 3050 APIENTRY 3051 NtGdiWidenPath(HDC hDC) 3052 { 3053 PPATH pPath; 3054 BOOL Ret = FALSE; 3055 PDC pdc = DC_LockDc(hDC); 3056 TRACE("NtGdiWidenPat Enter\n"); 3057 if (!pdc) 3058 { 3059 EngSetLastError(ERROR_INVALID_PARAMETER); 3060 return FALSE; 3061 } 3062 3063 pPath = PATH_WidenPath(pdc); 3064 if (pPath) 3065 { 3066 TRACE("WindenPath New Path\n"); 3067 PATH_Delete(pdc->dclevel.hPath); 3068 pdc->dclevel.hPath = pPath->BaseObject.hHmgr; 3069 Ret = TRUE; 3070 } 3071 DC_UnlockDc(pdc); 3072 TRACE("NtGdiWidenPat Ret %d\n",Ret); 3073 return Ret; 3074 } 3075 3076 /* EOF */ 3077