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