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