1 /* 2 * PROJECT: ReactOS Win32k Subsystem 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: win32ss/gdi/ntgdi/line.c 5 * PURPOSE: Line functions 6 * PROGRAMMERS: ... 7 */ 8 9 #include <win32k.h> 10 11 #define NDEBUG 12 #include <debug.h> 13 14 // Some code from the WINE project source (www.winehq.com) 15 16 VOID FASTCALL 17 AddPenLinesBounds(PDC dc, int count, POINT *points) 18 { 19 DWORD join, endcap; 20 RECTL bounds, rect; 21 LONG lWidth; 22 PBRUSH pbrLine; 23 24 /* Get BRUSH from current pen. */ 25 pbrLine = dc->dclevel.pbrLine; 26 ASSERT(pbrLine); 27 28 lWidth = pbrLine->lWidth; 29 30 // Setup bounds 31 bounds.left = bounds.top = INT_MAX; 32 bounds.right = bounds.bottom = INT_MIN; 33 34 if (((pbrLine->ulPenStyle & PS_TYPE_MASK) & PS_GEOMETRIC) || lWidth > 1) 35 { 36 /* Windows uses some heuristics to estimate the distance from the point that will be painted */ 37 lWidth = lWidth + 2; 38 endcap = (PS_ENDCAP_MASK & pbrLine->ulPenStyle); 39 join = (PS_JOIN_MASK & pbrLine->ulPenStyle); 40 if (join == PS_JOIN_MITER) 41 { 42 lWidth *= 5; 43 if (endcap == PS_ENDCAP_SQUARE) lWidth = (lWidth * 3 + 1) / 2; 44 } 45 else 46 { 47 if (endcap == PS_ENDCAP_SQUARE) lWidth -= lWidth / 4; 48 else lWidth = (lWidth + 1) / 2; 49 } 50 } 51 52 while (count-- > 0) 53 { 54 rect.left = points->x - lWidth; 55 rect.top = points->y - lWidth; 56 rect.right = points->x + lWidth + 1; 57 rect.bottom = points->y + lWidth + 1; 58 RECTL_bUnionRect(&bounds, &bounds, &rect); 59 points++; 60 } 61 62 DPRINT("APLB dc %p l %d t %d\n",dc,rect.left,rect.top); 63 DPRINT(" r %d b %d\n",rect.right,rect.bottom); 64 65 { 66 RECTL rcRgn; 67 if (dc->fs & DC_FLAG_DIRTY_RAO) CLIPPING_UpdateGCRegion(dc); 68 if (REGION_GetRgnBox(dc->prgnRao, &rcRgn)) 69 { 70 if (RECTL_bIntersectRect( &rcRgn, &rcRgn, &bounds )) IntUpdateBoundsRect(dc, &rcRgn); 71 } 72 else 73 IntUpdateBoundsRect(dc, &bounds); 74 } 75 } 76 77 // Should use Fx in Point 78 // 79 BOOL FASTCALL 80 IntGdiMoveToEx(DC *dc, 81 int X, 82 int Y, 83 LPPOINT Point) 84 { 85 PDC_ATTR pdcattr = dc->pdcattr; 86 if ( Point ) 87 { 88 if ( pdcattr->ulDirty_ & DIRTY_PTLCURRENT ) // Double hit! 89 { 90 Point->x = pdcattr->ptfxCurrent.x; // ret prev before change. 91 Point->y = pdcattr->ptfxCurrent.y; 92 IntDPtoLP ( dc, Point, 1); // Reconvert back. 93 } 94 else 95 { 96 Point->x = pdcattr->ptlCurrent.x; 97 Point->y = pdcattr->ptlCurrent.y; 98 } 99 } 100 pdcattr->ptlCurrent.x = X; 101 pdcattr->ptlCurrent.y = Y; 102 pdcattr->ptfxCurrent = pdcattr->ptlCurrent; 103 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx 104 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE); 105 106 return TRUE; 107 } 108 109 BOOL FASTCALL 110 GreMoveTo( HDC hdc, 111 INT x, 112 INT y, 113 LPPOINT pptOut) 114 { 115 BOOL Ret; 116 PDC dc; 117 if (!(dc = DC_LockDc(hdc))) 118 { 119 EngSetLastError(ERROR_INVALID_HANDLE); 120 return FALSE; 121 } 122 Ret = IntGdiMoveToEx(dc, x, y, pptOut); 123 DC_UnlockDc(dc); 124 return Ret; 125 } 126 127 // Should use Fx in pt 128 // 129 VOID FASTCALL 130 IntGetCurrentPositionEx(PDC dc, LPPOINT pt) 131 { 132 PDC_ATTR pdcattr = dc->pdcattr; 133 134 if ( pt ) 135 { 136 if (pdcattr->ulDirty_ & DIRTY_PTFXCURRENT) 137 { 138 pdcattr->ptfxCurrent = pdcattr->ptlCurrent; 139 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx 140 pdcattr->ulDirty_ &= ~(DIRTY_PTFXCURRENT|DIRTY_STYLESTATE); 141 } 142 pt->x = pdcattr->ptlCurrent.x; 143 pt->y = pdcattr->ptlCurrent.y; 144 } 145 } 146 147 BOOL FASTCALL 148 IntGdiLineTo(DC *dc, 149 int XEnd, 150 int YEnd) 151 { 152 SURFACE *psurf; 153 BOOL Ret = TRUE; 154 PBRUSH pbrLine; 155 RECTL Bounds; 156 POINT Points[2]; 157 PDC_ATTR pdcattr = dc->pdcattr; 158 ASSERT_DC_PREPARED(dc); 159 160 if (PATH_IsPathOpen(dc->dclevel)) 161 { 162 Ret = PATH_LineTo(dc, XEnd, YEnd); 163 } 164 else 165 { 166 psurf = dc->dclevel.pSurface; 167 if (NULL == psurf) 168 { 169 EngSetLastError(ERROR_INVALID_HANDLE); 170 return FALSE; 171 } 172 173 Points[0].x = pdcattr->ptlCurrent.x; 174 Points[0].y = pdcattr->ptlCurrent.y; 175 Points[1].x = XEnd; 176 Points[1].y = YEnd; 177 178 IntLPtoDP(dc, Points, 2); 179 180 /* The DCOrg is in device coordinates */ 181 Points[0].x += dc->ptlDCOrig.x; 182 Points[0].y += dc->ptlDCOrig.y; 183 Points[1].x += dc->ptlDCOrig.x; 184 Points[1].y += dc->ptlDCOrig.y; 185 186 Bounds.left = min(Points[0].x, Points[1].x); 187 Bounds.top = min(Points[0].y, Points[1].y); 188 Bounds.right = max(Points[0].x, Points[1].x); 189 Bounds.bottom = max(Points[0].y, Points[1].y); 190 191 /* Get BRUSH from current pen. */ 192 pbrLine = dc->dclevel.pbrLine; 193 ASSERT(pbrLine); 194 195 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR)) 196 { 197 DPRINT("Bounds dc %p l %d t %d\n",dc,Bounds.left,Bounds.top); 198 DPRINT(" r %d b %d\n",Bounds.right,Bounds.bottom); 199 AddPenLinesBounds(dc, 2, Points); 200 } 201 202 if (!(pbrLine->flAttrs & BR_IS_NULL)) 203 { 204 Ret = IntEngLineTo(&psurf->SurfObj, 205 (CLIPOBJ *)&dc->co, 206 &dc->eboLine.BrushObject, 207 Points[0].x, Points[0].y, 208 Points[1].x, Points[1].y, 209 &Bounds, 210 ROP2_TO_MIX(pdcattr->jROP2)); 211 } 212 213 } 214 215 if (Ret) 216 { 217 pdcattr->ptlCurrent.x = XEnd; 218 pdcattr->ptlCurrent.y = YEnd; 219 pdcattr->ptfxCurrent = pdcattr->ptlCurrent; 220 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx 221 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE); 222 } 223 224 return Ret; 225 } 226 227 BOOL FASTCALL 228 IntGdiPolyBezier(DC *dc, 229 LPPOINT pt, 230 DWORD Count) 231 { 232 BOOL ret = FALSE; // Default to FAILURE 233 234 if ( PATH_IsPathOpen(dc->dclevel) ) 235 { 236 return PATH_PolyBezier ( dc, pt, Count ); 237 } 238 239 /* We'll convert it into line segments and draw them using Polyline */ 240 { 241 POINT *Pts; 242 INT nOut; 243 244 Pts = GDI_Bezier ( pt, Count, &nOut ); 245 if ( Pts ) 246 { 247 ret = IntGdiPolyline(dc, Pts, nOut); 248 ExFreePoolWithTag(Pts, TAG_BEZIER); 249 } 250 } 251 252 return ret; 253 } 254 255 BOOL FASTCALL 256 IntGdiPolyBezierTo(DC *dc, 257 LPPOINT pt, 258 DWORD Count) 259 { 260 BOOL ret = FALSE; // Default to failure 261 PDC_ATTR pdcattr = dc->pdcattr; 262 263 if ( PATH_IsPathOpen(dc->dclevel) ) 264 ret = PATH_PolyBezierTo ( dc, pt, Count ); 265 else /* We'll do it using PolyBezier */ 266 { 267 POINT *npt; 268 npt = ExAllocatePoolWithTag(PagedPool, 269 sizeof(POINT) * (Count + 1), 270 TAG_BEZIER); 271 if ( npt ) 272 { 273 npt[0].x = pdcattr->ptlCurrent.x; 274 npt[0].y = pdcattr->ptlCurrent.y; 275 memcpy(npt + 1, pt, sizeof(POINT) * Count); 276 ret = IntGdiPolyBezier(dc, npt, Count+1); 277 ExFreePoolWithTag(npt, TAG_BEZIER); 278 } 279 } 280 if ( ret ) 281 { 282 pdcattr->ptlCurrent.x = pt[Count-1].x; 283 pdcattr->ptlCurrent.y = pt[Count-1].y; 284 pdcattr->ptfxCurrent = pdcattr->ptlCurrent; 285 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx 286 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE); 287 } 288 289 return ret; 290 } 291 292 BOOL FASTCALL 293 IntGdiPolyline(DC *dc, 294 LPPOINT pt, 295 int Count) 296 { 297 SURFACE *psurf; 298 BRUSH *pbrLine; 299 LPPOINT Points; 300 BOOL Ret = TRUE; 301 LONG i; 302 PDC_ATTR pdcattr = dc->pdcattr; 303 304 if (!dc->dclevel.pSurface) 305 { 306 return TRUE; 307 } 308 309 DC_vPrepareDCsForBlit(dc, NULL, NULL, NULL); 310 psurf = dc->dclevel.pSurface; 311 312 /* Get BRUSHOBJ from current pen. */ 313 pbrLine = dc->dclevel.pbrLine; 314 ASSERT(pbrLine); 315 316 if (!(pbrLine->flAttrs & BR_IS_NULL)) 317 { 318 Points = EngAllocMem(0, Count * sizeof(POINT), GDITAG_TEMP); 319 if (Points != NULL) 320 { 321 RtlCopyMemory(Points, pt, Count * sizeof(POINT)); 322 IntLPtoDP(dc, Points, Count); 323 324 /* Offset the array of points by the DC origin */ 325 for (i = 0; i < Count; i++) 326 { 327 Points[i].x += dc->ptlDCOrig.x; 328 Points[i].y += dc->ptlDCOrig.y; 329 } 330 331 if (dc->fs & (DC_ACCUM_APP|DC_ACCUM_WMGR)) 332 { 333 AddPenLinesBounds(dc, Count, Points); 334 } 335 336 Ret = IntEngPolyline(&psurf->SurfObj, 337 (CLIPOBJ *)&dc->co, 338 &dc->eboLine.BrushObject, 339 Points, 340 Count, 341 ROP2_TO_MIX(pdcattr->jROP2)); 342 343 EngFreeMem(Points); 344 } 345 else 346 { 347 Ret = FALSE; 348 } 349 } 350 351 DC_vFinishBlit(dc, NULL); 352 353 return Ret; 354 } 355 356 BOOL FASTCALL 357 IntGdiPolylineTo(DC *dc, 358 LPPOINT pt, 359 DWORD Count) 360 { 361 BOOL ret = FALSE; // Default to failure 362 PDC_ATTR pdcattr = dc->pdcattr; 363 364 if (PATH_IsPathOpen(dc->dclevel)) 365 { 366 ret = PATH_PolylineTo(dc, pt, Count); 367 } 368 else /* Do it using Polyline */ 369 { 370 POINT *pts = ExAllocatePoolWithTag(PagedPool, 371 sizeof(POINT) * (Count + 1), 372 TAG_SHAPE); 373 if ( pts ) 374 { 375 pts[0].x = pdcattr->ptlCurrent.x; 376 pts[0].y = pdcattr->ptlCurrent.y; 377 memcpy( pts + 1, pt, sizeof(POINT) * Count); 378 ret = IntGdiPolyline(dc, pts, Count + 1); 379 ExFreePoolWithTag(pts, TAG_SHAPE); 380 } 381 } 382 if ( ret ) 383 { 384 pdcattr->ptlCurrent.x = pt[Count-1].x; 385 pdcattr->ptlCurrent.y = pt[Count-1].y; 386 pdcattr->ptfxCurrent = pdcattr->ptlCurrent; 387 CoordLPtoDP(dc, &pdcattr->ptfxCurrent); // Update fx 388 pdcattr->ulDirty_ &= ~(DIRTY_PTLCURRENT|DIRTY_PTFXCURRENT|DIRTY_STYLESTATE); 389 } 390 391 return ret; 392 } 393 394 395 BOOL FASTCALL 396 IntGdiPolyPolyline(DC *dc, 397 LPPOINT pt, 398 PULONG PolyPoints, 399 DWORD Count) 400 { 401 ULONG i; 402 LPPOINT pts; 403 PULONG pc; 404 BOOL ret = FALSE; // Default to failure 405 pts = pt; 406 pc = PolyPoints; 407 408 if (PATH_IsPathOpen(dc->dclevel)) 409 { 410 return PATH_PolyPolyline( dc, pt, PolyPoints, Count ); 411 } 412 for (i = 0; i < Count; i++) 413 { 414 ret = IntGdiPolyline ( dc, pts, *pc ); 415 if (ret == FALSE) 416 { 417 return ret; 418 } 419 pts+=*pc++; 420 } 421 422 return ret; 423 } 424 425 /******************************************************************************/ 426 427 BOOL 428 APIENTRY 429 NtGdiLineTo(HDC hDC, 430 int XEnd, 431 int YEnd) 432 { 433 DC *dc; 434 BOOL Ret; 435 RECT rcLockRect ; 436 437 dc = DC_LockDc(hDC); 438 if (!dc) 439 { 440 EngSetLastError(ERROR_INVALID_HANDLE); 441 return FALSE; 442 } 443 444 rcLockRect.left = dc->pdcattr->ptlCurrent.x; 445 rcLockRect.top = dc->pdcattr->ptlCurrent.y; 446 rcLockRect.right = XEnd; 447 rcLockRect.bottom = YEnd; 448 449 IntLPtoDP(dc, &rcLockRect, 2); 450 451 /* The DCOrg is in device coordinates */ 452 rcLockRect.left += dc->ptlDCOrig.x; 453 rcLockRect.top += dc->ptlDCOrig.y; 454 rcLockRect.right += dc->ptlDCOrig.x; 455 rcLockRect.bottom += dc->ptlDCOrig.y; 456 457 DC_vPrepareDCsForBlit(dc, &rcLockRect, NULL, NULL); 458 459 Ret = IntGdiLineTo(dc, XEnd, YEnd); 460 461 DC_vFinishBlit(dc, NULL); 462 463 DC_UnlockDc(dc); 464 return Ret; 465 } 466 467 // FIXME: This function is completely broken 468 BOOL 469 APIENTRY 470 NtGdiPolyDraw( 471 IN HDC hdc, 472 IN LPPOINT lppt, 473 IN LPBYTE lpbTypes, 474 IN ULONG cCount) 475 { 476 PDC dc; 477 PDC_ATTR pdcattr; 478 POINT bzr[4]; 479 volatile PPOINT line_pts, line_pts_old, bzr_pts; 480 INT num_pts, num_bzr_pts, space, space_old, size; 481 ULONG i; 482 BOOL result = FALSE; 483 484 dc = DC_LockDc(hdc); 485 if (!dc) return FALSE; 486 pdcattr = dc->pdcattr; 487 488 if (pdcattr->ulDirty_ & (DIRTY_FILL | DC_BRUSH_DIRTY)) 489 DC_vUpdateFillBrush(dc); 490 491 if (pdcattr->ulDirty_ & (DIRTY_LINE | DC_PEN_DIRTY)) 492 DC_vUpdateLineBrush(dc); 493 494 if (!cCount) 495 { 496 DC_UnlockDc(dc); 497 return TRUE; 498 } 499 500 line_pts = NULL; 501 line_pts_old = NULL; 502 bzr_pts = NULL; 503 504 _SEH2_TRY 505 { 506 ProbeArrayForRead(lppt, sizeof(POINT), cCount, sizeof(LONG)); 507 ProbeArrayForRead(lpbTypes, sizeof(BYTE), cCount, sizeof(BYTE)); 508 509 if (PATH_IsPathOpen(dc->dclevel)) 510 { 511 result = PATH_PolyDraw(dc, (const POINT *)lppt, (const BYTE *)lpbTypes, cCount); 512 _SEH2_LEAVE; 513 } 514 515 /* Check for valid point types */ 516 for (i = 0; i < cCount; i++) 517 { 518 switch (lpbTypes[i]) 519 { 520 case PT_MOVETO: 521 case PT_LINETO | PT_CLOSEFIGURE: 522 case PT_LINETO: 523 break; 524 case PT_BEZIERTO: 525 if((i + 2 < cCount) && (lpbTypes[i + 1] == PT_BEZIERTO) && 526 ((lpbTypes[i + 2] & ~PT_CLOSEFIGURE) == PT_BEZIERTO)) 527 { 528 i += 2; 529 break; 530 } 531 default: 532 _SEH2_LEAVE; 533 } 534 } 535 536 space = cCount + 300; 537 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE); 538 if (line_pts == NULL) 539 { 540 result = FALSE; 541 _SEH2_LEAVE; 542 } 543 544 num_pts = 1; 545 546 line_pts[0].x = pdcattr->ptlCurrent.x; 547 line_pts[0].y = pdcattr->ptlCurrent.y; 548 549 for ( i = 0; i < cCount; i++ ) 550 { 551 switch (lpbTypes[i]) 552 { 553 case PT_MOVETO: 554 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts ); 555 num_pts = 0; 556 line_pts[num_pts++] = lppt[i]; 557 break; 558 case PT_LINETO: 559 case (PT_LINETO | PT_CLOSEFIGURE): 560 line_pts[num_pts++] = lppt[i]; 561 break; 562 case PT_BEZIERTO: 563 bzr[0].x = line_pts[num_pts - 1].x; 564 bzr[0].y = line_pts[num_pts - 1].y; 565 RtlCopyMemory( &bzr[1], &lppt[i], 3 * sizeof(POINT) ); 566 567 if ((bzr_pts = GDI_Bezier( bzr, 4, &num_bzr_pts ))) 568 { 569 size = num_pts + (cCount - i) + num_bzr_pts; 570 if (space < size) 571 { 572 space_old = space; 573 space = size * 2; 574 line_pts_old = line_pts; 575 line_pts = ExAllocatePoolWithTag(PagedPool, space * sizeof(POINT), TAG_SHAPE); 576 if (!line_pts) _SEH2_LEAVE; 577 RtlCopyMemory(line_pts, line_pts_old, space_old * sizeof(POINT)); 578 ExFreePoolWithTag(line_pts_old, TAG_SHAPE); 579 line_pts_old = NULL; 580 } 581 RtlCopyMemory( &line_pts[num_pts], &bzr_pts[1], (num_bzr_pts - 1) * sizeof(POINT) ); 582 num_pts += num_bzr_pts - 1; 583 ExFreePoolWithTag(bzr_pts, TAG_BEZIER); 584 bzr_pts = NULL; 585 } 586 i += 2; 587 break; 588 } 589 if (lpbTypes[i] & PT_CLOSEFIGURE) line_pts[num_pts++] = line_pts[0]; 590 } 591 592 if (num_pts >= 2) IntGdiPolyline( dc, line_pts, num_pts ); 593 IntGdiMoveToEx( dc, line_pts[num_pts - 1].x, line_pts[num_pts - 1].y, NULL ); 594 result = TRUE; 595 } 596 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 597 { 598 SetLastNtError(_SEH2_GetExceptionCode()); 599 } 600 _SEH2_END; 601 602 if (line_pts != NULL) 603 { 604 ExFreePoolWithTag(line_pts, TAG_SHAPE); 605 } 606 607 if ((line_pts_old != NULL) && (line_pts_old != line_pts)) 608 { 609 ExFreePoolWithTag(line_pts_old, TAG_SHAPE); 610 } 611 612 if (bzr_pts != NULL) 613 { 614 ExFreePoolWithTag(bzr_pts, TAG_BEZIER); 615 } 616 617 DC_UnlockDc(dc); 618 619 return result; 620 } 621 622 /* 623 * @implemented 624 */ 625 _Success_(return != FALSE) 626 BOOL 627 APIENTRY 628 NtGdiMoveTo( 629 IN HDC hdc, 630 IN INT x, 631 IN INT y, 632 OUT OPTIONAL LPPOINT pptOut) 633 { 634 PDC pdc; 635 BOOL Ret; 636 POINT Point; 637 638 pdc = DC_LockDc(hdc); 639 if (!pdc) return FALSE; 640 641 Ret = IntGdiMoveToEx(pdc, x, y, &Point); 642 643 if (Ret && pptOut) 644 { 645 _SEH2_TRY 646 { 647 ProbeForWrite(pptOut, sizeof(POINT), 1); 648 RtlCopyMemory(pptOut, &Point, sizeof(POINT)); 649 } 650 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 651 { 652 SetLastNtError(_SEH2_GetExceptionCode()); 653 Ret = FALSE; // CHECKME: is this correct? 654 } 655 _SEH2_END; 656 } 657 658 DC_UnlockDc(pdc); 659 660 return Ret; 661 } 662 663 /* EOF */ 664