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