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