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