1 /* 2 * Copyright 2001-2009, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 10 #include "DrawingEngine.h" 11 12 #include <Bitmap.h> 13 #include <stdio.h> 14 #include <algorithm> 15 #include <stack> 16 17 #include "DrawState.h" 18 #include "GlyphLayoutEngine.h" 19 #include "Painter.h" 20 #include "ServerBitmap.h" 21 #include "ServerCursor.h" 22 #include "RenderingBuffer.h" 23 24 #include "drawing_support.h" 25 26 27 #if DEBUG 28 # define ASSERT_PARALLEL_LOCKED() \ 29 { if (!IsParallelAccessLocked()) debugger("not parallel locked!"); } 30 # define ASSERT_EXCLUSIVE_LOCKED() \ 31 { if (!IsExclusiveAccessLocked()) debugger("not exclusive locked!"); } 32 #else 33 # define ASSERT_PARALLEL_LOCKED() 34 # define ASSERT_EXCLUSIVE_LOCKED() 35 #endif 36 37 38 static inline void 39 make_rect_valid(BRect& rect) 40 { 41 if (rect.left > rect.right) { 42 float temp = rect.left; 43 rect.left = rect.right; 44 rect.right = temp; 45 } 46 if (rect.top > rect.bottom) { 47 float temp = rect.top; 48 rect.top = rect.bottom; 49 rect.bottom = temp; 50 } 51 } 52 53 54 static inline void 55 extend_by_stroke_width(BRect& rect, float penSize) 56 { 57 // "- 0.5" because if stroke width == 1, we don't need to extend 58 float inset = -ceilf(penSize / 2.0 - 0.5); 59 rect.InsetBy(inset, inset); 60 } 61 62 63 class AutoFloatingOverlaysHider { 64 public: 65 AutoFloatingOverlaysHider(HWInterface* interface, const BRect& area) 66 : 67 fInterface(interface), 68 fHidden(interface->HideFloatingOverlays(area)) 69 { 70 } 71 72 AutoFloatingOverlaysHider(HWInterface* interface) 73 : 74 fInterface(interface), 75 fHidden(fInterface->HideFloatingOverlays()) 76 { 77 } 78 79 ~AutoFloatingOverlaysHider() 80 { 81 if (fHidden) 82 fInterface->ShowFloatingOverlays(); 83 } 84 85 bool WasHidden() const 86 { 87 return fHidden; 88 } 89 90 private: 91 HWInterface* fInterface; 92 bool fHidden; 93 94 }; 95 96 97 // #pragma mark - 98 99 100 DrawingEngine::DrawingEngine(HWInterface* interface) 101 : 102 fPainter(new Painter()), 103 fGraphicsCard(NULL), 104 fAvailableHWAccleration(0), 105 fSuspendSyncLevel(0), 106 fCopyToFront(true) 107 { 108 SetHWInterface(interface); 109 } 110 111 112 DrawingEngine::~DrawingEngine() 113 { 114 SetHWInterface(NULL); 115 delete fPainter; 116 } 117 118 119 // #pragma mark - locking 120 121 122 bool 123 DrawingEngine::LockParallelAccess() 124 { 125 return fGraphicsCard->LockParallelAccess(); 126 } 127 128 129 void 130 DrawingEngine::UnlockParallelAccess() 131 { 132 fGraphicsCard->UnlockParallelAccess(); 133 } 134 135 136 bool 137 DrawingEngine::LockExclusiveAccess() 138 { 139 return fGraphicsCard->LockExclusiveAccess(); 140 } 141 142 143 bool 144 DrawingEngine::IsExclusiveAccessLocked() 145 { 146 return fGraphicsCard->IsExclusiveAccessLocked(); 147 } 148 149 150 void 151 DrawingEngine::UnlockExclusiveAccess() 152 { 153 fGraphicsCard->UnlockExclusiveAccess(); 154 } 155 156 157 // #pragma mark - 158 159 160 void 161 DrawingEngine::FrameBufferChanged() 162 { 163 if (!fGraphicsCard) { 164 fPainter->DetachFromBuffer(); 165 fAvailableHWAccleration = 0; 166 return; 167 } 168 169 // NOTE: locking is probably bogus, since we are called 170 // in the thread that changed the frame buffer... 171 if (LockExclusiveAccess()) { 172 fPainter->AttachToBuffer(fGraphicsCard->DrawingBuffer()); 173 // available HW acceleration might have changed 174 fAvailableHWAccleration = fGraphicsCard->AvailableHWAcceleration(); 175 UnlockExclusiveAccess(); 176 } 177 } 178 179 180 void 181 DrawingEngine::SetHWInterface(HWInterface* interface) 182 { 183 if (fGraphicsCard == interface) 184 return; 185 186 if (fGraphicsCard) 187 fGraphicsCard->RemoveListener(this); 188 189 fGraphicsCard = interface; 190 191 if (fGraphicsCard) 192 fGraphicsCard->AddListener(this); 193 194 FrameBufferChanged(); 195 } 196 197 198 void 199 DrawingEngine::SetCopyToFrontEnabled(bool enable) 200 { 201 fCopyToFront = enable; 202 } 203 204 205 void 206 DrawingEngine::CopyToFront(/*const*/ BRegion& region) 207 { 208 fGraphicsCard->InvalidateRegion(region); 209 } 210 211 212 // #pragma mark - 213 214 215 //! the DrawingEngine needs to be locked! 216 void 217 DrawingEngine::ConstrainClippingRegion(const BRegion* region) 218 { 219 ASSERT_PARALLEL_LOCKED(); 220 221 fPainter->ConstrainClipping(region); 222 } 223 224 225 void 226 DrawingEngine::SetDrawState(const DrawState* state, int32 xOffset, 227 int32 yOffset) 228 { 229 fPainter->SetDrawState(state, xOffset, yOffset); 230 } 231 232 233 void 234 DrawingEngine::SetHighColor(const rgb_color& color) 235 { 236 fPainter->SetHighColor(color); 237 } 238 239 240 void 241 DrawingEngine::SetLowColor(const rgb_color& color) 242 { 243 fPainter->SetLowColor(color); 244 } 245 246 247 void 248 DrawingEngine::SetPenSize(float size) 249 { 250 fPainter->SetPenSize(size); 251 } 252 253 254 void 255 DrawingEngine::SetStrokeMode(cap_mode lineCap, join_mode joinMode, 256 float miterLimit) 257 { 258 fPainter->SetStrokeMode(lineCap, joinMode, miterLimit); 259 } 260 261 262 void 263 DrawingEngine::SetBlendingMode(source_alpha srcAlpha, alpha_function alphaFunc) 264 { 265 fPainter->SetBlendingMode(srcAlpha, alphaFunc); 266 } 267 268 269 void 270 DrawingEngine::SetPattern(const struct pattern& pattern) 271 { 272 fPainter->SetPattern(pattern, false); 273 } 274 275 276 void 277 DrawingEngine::SetDrawingMode(drawing_mode mode) 278 { 279 fPainter->SetDrawingMode(mode); 280 } 281 282 283 void 284 DrawingEngine::SetDrawingMode(drawing_mode mode, drawing_mode& oldMode) 285 { 286 oldMode = fPainter->DrawingMode(); 287 fPainter->SetDrawingMode(mode); 288 } 289 290 291 void 292 DrawingEngine::SetFont(const ServerFont& font) 293 { 294 fPainter->SetFont(font); 295 } 296 297 298 void 299 DrawingEngine::SetFont(const DrawState* state) 300 { 301 fPainter->SetFont(state); 302 } 303 304 305 // #pragma mark - 306 307 308 void 309 DrawingEngine::SuspendAutoSync() 310 { 311 ASSERT_PARALLEL_LOCKED(); 312 313 fSuspendSyncLevel++; 314 } 315 316 317 void 318 DrawingEngine::Sync() 319 { 320 ASSERT_PARALLEL_LOCKED(); 321 322 fSuspendSyncLevel--; 323 if (fSuspendSyncLevel == 0) 324 fGraphicsCard->Sync(); 325 } 326 327 328 // #pragma mark - 329 330 331 // CopyRegion() does a topological sort of the rects in the 332 // region. The algorithm was suggested by Ingo Weinhold. 333 // It compares each rect with each rect and builds a tree 334 // of successors so we know the order in which they can be copied. 335 // For example, let's suppose these rects are in a BRegion: 336 // ************ 337 // * B * 338 // ************ 339 // ************* 340 // * * 341 // * A **************** 342 // * ** * 343 // ************** * 344 // * C * 345 // * * 346 // * * 347 // *************** 348 // When copying stuff from LEFT TO RIGHT, TOP TO BOTTOM, the 349 // result of the sort will be C, A, B. For this direction, we search 350 // for the rects that have no neighbors to their right and to their 351 // bottom, These can be copied without drawing into the area of 352 // rects yet to be copied. If you move from RIGHT TO LEFT, BOTTOM TO TOP, 353 // you go look for the ones that have no neighbors to their top and left. 354 // 355 // Here I draw some rays to illustrate LEFT TO RIGHT, TOP TO BOTTOM: 356 // ************ 357 // * B * 358 // ************ 359 // ************* 360 // * * 361 // * A ****************----------------- 362 // * ** * 363 // ************** * 364 // * C * 365 // * * 366 // * * 367 // *************** 368 // | 369 // | 370 // | 371 // | 372 // There are no rects in the area defined by the rays to the right 373 // and bottom of rect C, so that's the one we want to copy first 374 // (for positive x and y offsets). 375 // Since A is to the left of C and B is to the top of C, The "node" 376 // for C will point to the nodes of A and B as its "successors". Therefor, 377 // A and B will have an "indegree" of 1 for C pointing to them. C will 378 // have an "indegree" of 0, because there was no rect to which C 379 // was to the left or top of. When comparing A and B, neither is left 380 // or top from the other and in the sense that the algorithm cares about. 381 382 // NOTE: comparison of coordinates assumes that rects don't overlap 383 // and don't share the actual edge either (as is the case in BRegions). 384 385 struct node { 386 node() 387 { 388 pointers = NULL; 389 } 390 391 node(const BRect& r, int32 maxPointers) 392 { 393 init(r, maxPointers); 394 } 395 396 ~node() 397 { 398 delete [] pointers; 399 } 400 401 void init(const BRect& r, int32 maxPointers) 402 { 403 rect = r; 404 pointers = new node*[maxPointers]; 405 in_degree = 0; 406 next_pointer = 0; 407 } 408 409 void push(node* node) 410 { 411 pointers[next_pointer] = node; 412 next_pointer++; 413 } 414 415 node* top() 416 { 417 return pointers[next_pointer]; 418 } 419 420 node* pop() 421 { 422 node* ret = top(); 423 next_pointer--; 424 return ret; 425 } 426 427 BRect rect; 428 int32 in_degree; 429 node** pointers; 430 int32 next_pointer; 431 }; 432 433 434 static bool 435 is_left_of(const BRect& a, const BRect& b) 436 { 437 return (a.right < b.left); 438 } 439 440 441 static bool 442 is_above(const BRect& a, const BRect& b) 443 { 444 return (a.bottom < b.top); 445 } 446 447 448 void 449 DrawingEngine::CopyRegion(/*const*/ BRegion* region, int32 xOffset, 450 int32 yOffset) 451 { 452 ASSERT_EXCLUSIVE_LOCKED(); 453 454 BRect frame = region->Frame(); 455 frame = frame | frame.OffsetByCopy(xOffset, yOffset); 456 457 AutoFloatingOverlaysHider _(fGraphicsCard, frame); 458 459 int32 count = region->CountRects(); 460 461 // TODO: make this step unnecessary 462 // (by using different stack impl inside node) 463 node nodes[count]; 464 for (int32 i= 0; i < count; i++) { 465 nodes[i].init(region->RectAt(i), count); 466 } 467 468 for (int32 i = 0; i < count; i++) { 469 BRect a = region->RectAt(i); 470 for (int32 k = i + 1; k < count; k++) { 471 BRect b = region->RectAt(k); 472 int cmp = 0; 473 // compare horizontally 474 if (xOffset > 0) { 475 if (is_left_of(a, b)) { 476 cmp -= 1; 477 } else if (is_left_of(b, a)) { 478 cmp += 1; 479 } 480 } else if (xOffset < 0) { 481 if (is_left_of(a, b)) { 482 cmp += 1; 483 } else if (is_left_of(b, a)) { 484 cmp -= 1; 485 } 486 } 487 // compare vertically 488 if (yOffset > 0) { 489 if (is_above(a, b)) { 490 cmp -= 1; 491 } else if (is_above(b, a)) { 492 cmp += 1; 493 } 494 } else if (yOffset < 0) { 495 if (is_above(a, b)) { 496 cmp += 1; 497 } else if (is_above(b, a)) { 498 cmp -= 1; 499 } 500 } 501 // add appropriate node as successor 502 if (cmp > 0) { 503 nodes[i].push(&nodes[k]); 504 nodes[k].in_degree++; 505 } else if (cmp < 0) { 506 nodes[k].push(&nodes[i]); 507 nodes[i].in_degree++; 508 } 509 } 510 } 511 // put all nodes onto a stack that have an "indegree" count of zero 512 std::stack<node*> inDegreeZeroNodes; 513 for (int32 i = 0; i < count; i++) { 514 if (nodes[i].in_degree == 0) { 515 inDegreeZeroNodes.push(&nodes[i]); 516 } 517 } 518 // pop the rects from the stack, do the actual copy operation 519 // and decrease the "indegree" count of the other rects not 520 // currently on the stack and to which the current rect pointed 521 // to. If their "indegree" count reaches zero, put them onto the 522 // stack as well. 523 524 clipping_rect* sortedRectList = NULL; 525 int32 nextSortedIndex = 0; 526 527 if (fAvailableHWAccleration & HW_ACC_COPY_REGION) 528 sortedRectList = new clipping_rect[count]; 529 530 while (!inDegreeZeroNodes.empty()) { 531 node* n = inDegreeZeroNodes.top(); 532 inDegreeZeroNodes.pop(); 533 534 // do the software implementation or add to sorted 535 // rect list for using the HW accelerated version 536 // later 537 if (sortedRectList) { 538 sortedRectList[nextSortedIndex].left = (int32)n->rect.left; 539 sortedRectList[nextSortedIndex].top = (int32)n->rect.top; 540 sortedRectList[nextSortedIndex].right = (int32)n->rect.right; 541 sortedRectList[nextSortedIndex].bottom = (int32)n->rect.bottom; 542 nextSortedIndex++; 543 } else { 544 BRect touched = CopyRect(n->rect, xOffset, yOffset); 545 fGraphicsCard->Invalidate(touched); 546 } 547 548 for (int32 k = 0; k < n->next_pointer; k++) { 549 n->pointers[k]->in_degree--; 550 if (n->pointers[k]->in_degree == 0) 551 inDegreeZeroNodes.push(n->pointers[k]); 552 } 553 } 554 555 // trigger the HW accelerated version if it was available 556 if (sortedRectList) { 557 fGraphicsCard->CopyRegion(sortedRectList, count, xOffset, yOffset); 558 if (fGraphicsCard->IsDoubleBuffered()) { 559 fGraphicsCard->Invalidate( 560 region->Frame().OffsetByCopy(xOffset, yOffset)); 561 } 562 } 563 564 delete[] sortedRectList; 565 } 566 567 568 void 569 DrawingEngine::InvertRect(BRect r) 570 { 571 ASSERT_PARALLEL_LOCKED(); 572 573 make_rect_valid(r); 574 r = fPainter->ClipRect(r); 575 if (!r.IsValid()) 576 return; 577 578 AutoFloatingOverlaysHider _(fGraphicsCard, r); 579 580 // try hardware optimized version first 581 if (fAvailableHWAccleration & HW_ACC_INVERT_REGION) { 582 BRegion region(r); 583 region.IntersectWith(fPainter->ClippingRegion()); 584 fGraphicsCard->InvertRegion(region); 585 } else { 586 fPainter->InvertRect(r); 587 } 588 589 _CopyToFront(r); 590 } 591 592 593 void 594 DrawingEngine::DrawBitmap(ServerBitmap* bitmap, const BRect& bitmapRect, 595 const BRect& viewRect, uint32 options) 596 { 597 ASSERT_PARALLEL_LOCKED(); 598 599 BRect clipped = fPainter->ClipRect(viewRect); 600 if (clipped.IsValid()) { 601 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 602 603 fPainter->DrawBitmap(bitmap, bitmapRect, viewRect, options); 604 605 _CopyToFront(clipped); 606 } 607 } 608 609 610 void 611 DrawingEngine::DrawArc(BRect r, const float& angle, const float& span, 612 bool filled) 613 { 614 ASSERT_PARALLEL_LOCKED(); 615 616 make_rect_valid(r); 617 fPainter->AlignEllipseRect(&r, filled); 618 BRect clipped(r); 619 620 if (!filled) 621 extend_by_stroke_width(clipped, fPainter->PenSize()); 622 623 clipped = fPainter->ClipRect(r); 624 625 if (clipped.IsValid()) { 626 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 627 628 float xRadius = r.Width() / 2.0; 629 float yRadius = r.Height() / 2.0; 630 BPoint center(r.left + xRadius, 631 r.top + yRadius); 632 633 if (filled) 634 fPainter->FillArc(center, xRadius, yRadius, angle, span); 635 else 636 fPainter->StrokeArc(center, xRadius, yRadius, angle, span); 637 638 _CopyToFront(clipped); 639 } 640 } 641 642 643 void 644 DrawingEngine::FillArc(BRect r, const float& angle, const float& span, 645 const BGradient& gradient) 646 { 647 ASSERT_PARALLEL_LOCKED(); 648 649 make_rect_valid(r); 650 fPainter->AlignEllipseRect(&r, true); 651 BRect clipped(r); 652 653 clipped = fPainter->ClipRect(r); 654 655 if (clipped.IsValid()) { 656 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 657 658 float xRadius = r.Width() / 2.0; 659 float yRadius = r.Height() / 2.0; 660 BPoint center(r.left + xRadius, 661 r.top + yRadius); 662 663 fPainter->FillArc(center, xRadius, yRadius, angle, span, gradient); 664 665 _CopyToFront(clipped); 666 } 667 } 668 669 670 void 671 DrawingEngine::DrawBezier(BPoint* pts, bool filled) 672 { 673 ASSERT_PARALLEL_LOCKED(); 674 675 // TODO: figure out bounds and hide cursor depending on that 676 AutoFloatingOverlaysHider _(fGraphicsCard); 677 678 BRect touched = fPainter->DrawBezier(pts, filled); 679 680 _CopyToFront(touched); 681 } 682 683 684 void 685 DrawingEngine::FillBezier(BPoint* pts, const BGradient& gradient) 686 { 687 ASSERT_PARALLEL_LOCKED(); 688 689 // TODO: figure out bounds and hide cursor depending on that 690 AutoFloatingOverlaysHider _(fGraphicsCard); 691 692 BRect touched = fPainter->FillBezier(pts, gradient); 693 694 _CopyToFront(touched); 695 } 696 697 698 void 699 DrawingEngine::DrawEllipse(BRect r, bool filled) 700 { 701 ASSERT_PARALLEL_LOCKED(); 702 703 make_rect_valid(r); 704 BRect clipped = r; 705 fPainter->AlignEllipseRect(&clipped, filled); 706 707 if (!filled) 708 extend_by_stroke_width(clipped, fPainter->PenSize()); 709 710 clipped.left = floorf(clipped.left); 711 clipped.top = floorf(clipped.top); 712 clipped.right = ceilf(clipped.right); 713 clipped.bottom = ceilf(clipped.bottom); 714 715 clipped = fPainter->ClipRect(clipped); 716 717 if (clipped.IsValid()) { 718 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 719 720 fPainter->DrawEllipse(r, filled); 721 722 _CopyToFront(clipped); 723 } 724 } 725 726 727 void 728 DrawingEngine::FillEllipse(BRect r, const BGradient& gradient) 729 { 730 ASSERT_PARALLEL_LOCKED(); 731 732 make_rect_valid(r); 733 BRect clipped = r; 734 fPainter->AlignEllipseRect(&clipped, true); 735 736 clipped.left = floorf(clipped.left); 737 clipped.top = floorf(clipped.top); 738 clipped.right = ceilf(clipped.right); 739 clipped.bottom = ceilf(clipped.bottom); 740 741 clipped = fPainter->ClipRect(clipped); 742 743 if (clipped.IsValid()) { 744 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 745 746 fPainter->FillEllipse(r, gradient); 747 748 _CopyToFront(clipped); 749 } 750 } 751 752 753 void 754 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, BRect bounds, 755 bool filled, bool closed) 756 { 757 ASSERT_PARALLEL_LOCKED(); 758 759 make_rect_valid(bounds); 760 if (!filled) 761 extend_by_stroke_width(bounds, fPainter->PenSize()); 762 bounds = fPainter->ClipRect(bounds); 763 if (bounds.IsValid()) { 764 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 765 766 fPainter->DrawPolygon(ptlist, numpts, filled, closed); 767 768 _CopyToFront(bounds); 769 } 770 } 771 772 773 void 774 DrawingEngine::FillPolygon(BPoint* ptlist, int32 numpts, BRect bounds, 775 const BGradient& gradient, bool closed) 776 { 777 ASSERT_PARALLEL_LOCKED(); 778 779 make_rect_valid(bounds); 780 bounds = fPainter->ClipRect(bounds); 781 if (bounds.IsValid()) { 782 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 783 784 fPainter->FillPolygon(ptlist, numpts, gradient, closed); 785 786 _CopyToFront(bounds); 787 } 788 } 789 790 791 // #pragma mark - rgb_color 792 793 794 void 795 DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color& color) 796 { 797 StrokeLine(pt, pt, color); 798 } 799 800 801 /*! This function is only used by Decorators, 802 it assumes a one pixel wide line 803 */ 804 void 805 DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end, 806 const rgb_color& color) 807 { 808 ASSERT_PARALLEL_LOCKED(); 809 810 BRect touched(start, end); 811 make_rect_valid(touched); 812 touched = fPainter->ClipRect(touched); 813 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 814 815 if (!fPainter->StraightLine(start, end, color)) { 816 rgb_color previousColor = fPainter->HighColor(); 817 drawing_mode previousMode = fPainter->DrawingMode(); 818 819 fPainter->SetHighColor(color); 820 fPainter->SetDrawingMode(B_OP_OVER); 821 fPainter->StrokeLine(start, end); 822 823 fPainter->SetDrawingMode(previousMode); 824 fPainter->SetHighColor(previousColor); 825 } 826 827 _CopyToFront(touched); 828 } 829 830 831 //! This function is used to draw a one pixel wide rect 832 void 833 DrawingEngine::StrokeRect(BRect r, const rgb_color& color) 834 { 835 ASSERT_PARALLEL_LOCKED(); 836 837 make_rect_valid(r); 838 BRect clipped = fPainter->ClipRect(r); 839 if (clipped.IsValid()) { 840 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 841 842 fPainter->StrokeRect(r, color); 843 844 _CopyToFront(clipped); 845 } 846 } 847 848 849 void 850 DrawingEngine::FillRect(BRect r, const rgb_color& color) 851 { 852 ASSERT_PARALLEL_LOCKED(); 853 854 // NOTE: Write locking because we might use HW acceleration. 855 // This needs to be investigated, I'm doing this because of 856 // gut feeling. 857 make_rect_valid(r); 858 r = fPainter->ClipRect(r); 859 if (!r.IsValid()) 860 return; 861 862 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 863 864 // try hardware optimized version first 865 if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { 866 BRegion region(r); 867 region.IntersectWith(fPainter->ClippingRegion()); 868 fGraphicsCard->FillRegion(region, color, 869 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 870 } else { 871 fPainter->FillRect(r, color); 872 } 873 874 _CopyToFront(r); 875 } 876 877 878 void 879 DrawingEngine::FillRegion(BRegion& r, const rgb_color& color) 880 { 881 ASSERT_PARALLEL_LOCKED(); 882 883 // NOTE: region expected to be already clipped correctly!! 884 BRect frame = r.Frame(); 885 if (!fPainter->Bounds().Contains(frame)) { 886 // NOTE: I am not quite sure yet how this can happen, but apparently it 887 // can (see bug #634). 888 // This function is used for internal app_server painting, in the case of 889 // bug #634, the background of views is painted. But the view region 890 // should never be outside the frame buffer bounds. 891 // char message[1024]; 892 // BRect bounds = fPainter->Bounds(); 893 // sprintf(message, "FillRegion() - painter: (%d, %d)->(%d, %d), region: (%d, %d)->(%d, %d)", 894 // (int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom, 895 // (int)frame.left, (int)frame.top, (int)frame.right, (int)frame.bottom); 896 // debugger(message); 897 return; 898 } 899 900 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, frame); 901 902 // try hardware optimized version first 903 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 904 && frame.Width() * frame.Height() > 100) { 905 fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 906 || overlaysHider.WasHidden()); 907 } else { 908 int32 count = r.CountRects(); 909 for (int32 i = 0; i < count; i++) 910 fPainter->FillRectNoClipping(r.RectAtInt(i), color); 911 } 912 913 _CopyToFront(frame); 914 } 915 916 917 // #pragma mark - DrawState 918 919 920 void 921 DrawingEngine::StrokeRect(BRect r) 922 { 923 ASSERT_PARALLEL_LOCKED(); 924 925 // support invalid rects 926 make_rect_valid(r); 927 BRect clipped(r); 928 extend_by_stroke_width(clipped, fPainter->PenSize()); 929 clipped = fPainter->ClipRect(clipped); 930 if (clipped.IsValid()) { 931 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 932 933 fPainter->StrokeRect(r); 934 935 _CopyToFront(clipped); 936 } 937 } 938 939 940 void 941 DrawingEngine::FillRect(BRect r) 942 { 943 ASSERT_PARALLEL_LOCKED(); 944 945 make_rect_valid(r); 946 r = fPainter->AlignAndClipRect(r); 947 if (!r.IsValid()) 948 return; 949 950 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 951 952 bool doInSoftware = true; 953 if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { 954 // try hardware optimized version first 955 // if the rect is large enough 956 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 957 if (fPainter->Pattern() == B_SOLID_HIGH 958 && (fPainter->DrawingMode() == B_OP_COPY 959 || fPainter->DrawingMode() == B_OP_OVER)) { 960 BRegion region(r); 961 region.IntersectWith(fPainter->ClippingRegion()); 962 fGraphicsCard->FillRegion(region, fPainter->HighColor(), 963 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 964 doInSoftware = false; 965 } else if (fPainter->Pattern() == B_SOLID_LOW 966 && fPainter->DrawingMode() == B_OP_COPY) { 967 BRegion region(r); 968 region.IntersectWith(fPainter->ClippingRegion()); 969 fGraphicsCard->FillRegion(region, fPainter->LowColor(), 970 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 971 doInSoftware = false; 972 } 973 } 974 } 975 976 if (doInSoftware && (fAvailableHWAccleration & HW_ACC_INVERT_REGION) != 0 977 && fPainter->Pattern() == B_SOLID_HIGH 978 && fPainter->DrawingMode() == B_OP_INVERT) { 979 BRegion region(r); 980 region.IntersectWith(fPainter->ClippingRegion()); 981 fGraphicsCard->InvertRegion(region); 982 doInSoftware = false; 983 } 984 985 if (doInSoftware) 986 fPainter->FillRect(r); 987 988 _CopyToFront(r); 989 } 990 991 992 void 993 DrawingEngine::FillRect(BRect r, const BGradient& gradient) 994 { 995 ASSERT_PARALLEL_LOCKED(); 996 997 make_rect_valid(r); 998 r = fPainter->AlignAndClipRect(r); 999 if (!r.IsValid()) 1000 return; 1001 1002 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 1003 1004 fPainter->FillRect(r, gradient); 1005 1006 _CopyToFront(r); 1007 } 1008 1009 1010 void 1011 DrawingEngine::FillRegion(BRegion& r) 1012 { 1013 ASSERT_PARALLEL_LOCKED(); 1014 1015 BRect clipped = fPainter->ClipRect(r.Frame()); 1016 if (!clipped.IsValid()) 1017 return; 1018 1019 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped); 1020 1021 bool doInSoftware = true; 1022 // try hardware optimized version first 1023 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 1024 if (fPainter->Pattern() == B_SOLID_HIGH 1025 && (fPainter->DrawingMode() == B_OP_COPY 1026 || fPainter->DrawingMode() == B_OP_OVER)) { 1027 r.IntersectWith(fPainter->ClippingRegion()); 1028 fGraphicsCard->FillRegion(r, fPainter->HighColor(), 1029 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 1030 doInSoftware = false; 1031 } else if (fPainter->Pattern() == B_SOLID_LOW 1032 && fPainter->DrawingMode() == B_OP_COPY) { 1033 r.IntersectWith(fPainter->ClippingRegion()); 1034 fGraphicsCard->FillRegion(r, fPainter->LowColor(), 1035 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 1036 doInSoftware = false; 1037 } 1038 } 1039 1040 if (doInSoftware && (fAvailableHWAccleration & HW_ACC_INVERT_REGION) != 0 1041 && fPainter->Pattern() == B_SOLID_HIGH 1042 && fPainter->DrawingMode() == B_OP_INVERT) { 1043 r.IntersectWith(fPainter->ClippingRegion()); 1044 fGraphicsCard->InvertRegion(r); 1045 doInSoftware = false; 1046 } 1047 1048 if (doInSoftware) { 1049 BRect touched = fPainter->FillRect(r.RectAt(0)); 1050 1051 int32 count = r.CountRects(); 1052 for (int32 i = 1; i < count; i++) 1053 touched = touched | fPainter->FillRect(r.RectAt(i)); 1054 } 1055 1056 _CopyToFront(r.Frame()); 1057 } 1058 1059 1060 void 1061 DrawingEngine::FillRegion(BRegion& r, const BGradient& gradient) 1062 { 1063 ASSERT_PARALLEL_LOCKED(); 1064 1065 BRect clipped = fPainter->ClipRect(r.Frame()); 1066 if (!clipped.IsValid()) 1067 return; 1068 1069 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped); 1070 1071 BRect touched = fPainter->FillRect(r.RectAt(0), gradient); 1072 1073 int32 count = r.CountRects(); 1074 for (int32 i = 1; i < count; i++) 1075 touched = touched | fPainter->FillRect(r.RectAt(i), gradient); 1076 1077 _CopyToFront(r.Frame()); 1078 } 1079 1080 1081 void 1082 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled) 1083 { 1084 ASSERT_PARALLEL_LOCKED(); 1085 1086 // NOTE: the stroke does not extend past "r" in R5, 1087 // though I consider this unexpected behaviour. 1088 make_rect_valid(r); 1089 BRect clipped = fPainter->ClipRect(r); 1090 1091 clipped.left = floorf(clipped.left); 1092 clipped.top = floorf(clipped.top); 1093 clipped.right = ceilf(clipped.right); 1094 clipped.bottom = ceilf(clipped.bottom); 1095 1096 if (clipped.IsValid()) { 1097 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1098 1099 BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad) 1100 : fPainter->StrokeRoundRect(r, xrad, yrad); 1101 1102 _CopyToFront(touched); 1103 } 1104 } 1105 1106 1107 void 1108 DrawingEngine::FillRoundRect(BRect r, float xrad, float yrad, 1109 const BGradient& gradient) 1110 { 1111 ASSERT_PARALLEL_LOCKED(); 1112 1113 // NOTE: the stroke does not extend past "r" in R5, 1114 // though I consider this unexpected behaviour. 1115 make_rect_valid(r); 1116 BRect clipped = fPainter->ClipRect(r); 1117 1118 clipped.left = floorf(clipped.left); 1119 clipped.top = floorf(clipped.top); 1120 clipped.right = ceilf(clipped.right); 1121 clipped.bottom = ceilf(clipped.bottom); 1122 1123 if (clipped.IsValid()) { 1124 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1125 1126 BRect touched = fPainter->FillRoundRect(r, xrad, yrad, gradient); 1127 1128 _CopyToFront(touched); 1129 } 1130 } 1131 1132 1133 void 1134 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 1135 const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled) 1136 { 1137 ASSERT_PARALLEL_LOCKED(); 1138 1139 // NOTE: hides cursor regardless of if and where 1140 // shape is drawn on screen, TODO: optimize 1141 AutoFloatingOverlaysHider _(fGraphicsCard); 1142 1143 BRect touched = fPainter->DrawShape(opCount, opList, ptCount, ptList, 1144 filled); 1145 1146 _CopyToFront(touched); 1147 } 1148 1149 1150 void 1151 DrawingEngine::FillShape(const BRect& bounds, int32 opCount, 1152 const uint32* opList, int32 ptCount, const BPoint* ptList, 1153 const BGradient& gradient) 1154 { 1155 ASSERT_PARALLEL_LOCKED(); 1156 1157 // NOTE: hides cursor regardless of if and where 1158 // shape is drawn on screen, TODO: optimize 1159 AutoFloatingOverlaysHider _(fGraphicsCard); 1160 1161 BRect touched = fPainter->FillShape(opCount, opList, ptCount, ptList, 1162 gradient); 1163 1164 _CopyToFront(touched); 1165 } 1166 1167 1168 void 1169 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled) 1170 { 1171 ASSERT_PARALLEL_LOCKED(); 1172 1173 BRect clipped(bounds); 1174 if (!filled) 1175 extend_by_stroke_width(clipped, fPainter->PenSize()); 1176 clipped = fPainter->ClipRect(clipped); 1177 if (clipped.IsValid()) { 1178 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1179 1180 if (filled) 1181 fPainter->FillTriangle(pts[0], pts[1], pts[2]); 1182 else 1183 fPainter->StrokeTriangle(pts[0], pts[1], pts[2]); 1184 1185 _CopyToFront(clipped); 1186 } 1187 } 1188 1189 1190 void 1191 DrawingEngine::FillTriangle(BPoint* pts, const BRect& bounds, 1192 const BGradient& gradient) 1193 { 1194 ASSERT_PARALLEL_LOCKED(); 1195 1196 BRect clipped(bounds); 1197 clipped = fPainter->ClipRect(clipped); 1198 if (clipped.IsValid()) { 1199 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1200 1201 fPainter->FillTriangle(pts[0], pts[1], pts[2], gradient); 1202 1203 _CopyToFront(clipped); 1204 } 1205 } 1206 1207 1208 void 1209 DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end) 1210 { 1211 ASSERT_PARALLEL_LOCKED(); 1212 1213 BRect touched(start, end); 1214 make_rect_valid(touched); 1215 extend_by_stroke_width(touched, fPainter->PenSize()); 1216 touched = fPainter->ClipRect(touched); 1217 if (touched.IsValid()) { 1218 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 1219 1220 fPainter->StrokeLine(start, end); 1221 1222 _CopyToFront(touched); 1223 } 1224 } 1225 1226 1227 void 1228 DrawingEngine::StrokeLineArray(int32 numLines, 1229 const ViewLineArrayInfo *lineData) 1230 { 1231 ASSERT_PARALLEL_LOCKED(); 1232 1233 if (!lineData || numLines <= 0) 1234 return; 1235 1236 // figure out bounding box for line array 1237 const ViewLineArrayInfo* data = (const ViewLineArrayInfo*)&lineData[0]; 1238 BRect touched(min_c(data->startPoint.x, data->endPoint.x), 1239 min_c(data->startPoint.y, data->endPoint.y), 1240 max_c(data->startPoint.x, data->endPoint.x), 1241 max_c(data->startPoint.y, data->endPoint.y)); 1242 1243 for (int32 i = 1; i < numLines; i++) { 1244 data = (const ViewLineArrayInfo*)&lineData[i]; 1245 BRect box(min_c(data->startPoint.x, data->endPoint.x), 1246 min_c(data->startPoint.y, data->endPoint.y), 1247 max_c(data->startPoint.x, data->endPoint.x), 1248 max_c(data->startPoint.y, data->endPoint.y)); 1249 touched = touched | box; 1250 } 1251 extend_by_stroke_width(touched, fPainter->PenSize()); 1252 touched = fPainter->ClipRect(touched); 1253 if (touched.IsValid()) { 1254 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 1255 1256 data = (const ViewLineArrayInfo*)&(lineData[0]); 1257 1258 // store current graphics state, we mess with the 1259 // high color and pattern... 1260 rgb_color oldColor = fPainter->HighColor(); 1261 struct pattern pattern = fPainter->Pattern(); 1262 1263 fPainter->SetHighColor(data->color); 1264 fPainter->SetPattern(B_SOLID_HIGH); 1265 fPainter->StrokeLine(data->startPoint, data->endPoint); 1266 1267 for (int32 i = 1; i < numLines; i++) { 1268 data = (const ViewLineArrayInfo*)&(lineData[i]); 1269 fPainter->SetHighColor(data->color); 1270 fPainter->StrokeLine(data->startPoint, data->endPoint); 1271 } 1272 1273 // restore correct drawing state highcolor and pattern 1274 fPainter->SetHighColor(oldColor); 1275 fPainter->SetPattern(pattern); 1276 1277 _CopyToFront(touched); 1278 } 1279 } 1280 1281 1282 // #pragma mark - 1283 1284 1285 BPoint 1286 DrawingEngine::DrawString(const char* string, int32 length, 1287 const BPoint& pt, escapement_delta* delta) 1288 { 1289 ASSERT_PARALLEL_LOCKED(); 1290 1291 BPoint penLocation = pt; 1292 1293 // try a fast clipping path 1294 if (fPainter->ClippingRegion() && fPainter->Font().Rotation() == 0.0f) { 1295 float fontSize = fPainter->Font().Size(); 1296 BRect clippingFrame = fPainter->ClippingRegion()->Frame(); 1297 if (pt.x - fontSize > clippingFrame.right 1298 || pt.y + fontSize < clippingFrame.top 1299 || pt.y - fontSize > clippingFrame.bottom) { 1300 penLocation.x += StringWidth(string, length, delta); 1301 return penLocation; 1302 } 1303 } 1304 1305 // use a FontCacheRefernece to speed up the second pass of 1306 // drawing the string 1307 FontCacheReference cacheReference; 1308 1309 //bigtime_t now = system_time(); 1310 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. 1311 // Cursiously, the DrawString after it is actually faster!?! 1312 // TODO: make the availability of the hardware cursor part of the 1313 // HW acceleration flags and skip all calculations for HideFloatingOverlays 1314 // in case we don't have one. 1315 // TODO: Watch out about penLocation and use Painter::PenLocation() when 1316 // not using BoundindBox anymore. 1317 BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta, 1318 &cacheReference); 1319 // stop here if we're supposed to render outside of the clipping 1320 b = fPainter->ClipRect(b); 1321 if (b.IsValid()) { 1322 //printf("bounding box '%s': %lld µs\n", string, system_time() - now); 1323 AutoFloatingOverlaysHider _(fGraphicsCard, b); 1324 1325 //now = system_time(); 1326 BRect touched = fPainter->DrawString(string, length, pt, delta, 1327 &cacheReference); 1328 //printf("drawing string: %lld µs\n", system_time() - now); 1329 1330 _CopyToFront(touched); 1331 } 1332 1333 return penLocation; 1334 } 1335 1336 1337 float 1338 DrawingEngine::StringWidth(const char* string, int32 length, 1339 escapement_delta* delta) 1340 { 1341 return fPainter->StringWidth(string, length, delta); 1342 } 1343 1344 1345 float 1346 DrawingEngine::StringWidth(const char* string, int32 length, 1347 const ServerFont& font, escapement_delta* delta) 1348 { 1349 return font.StringWidth(string, length, delta); 1350 } 1351 1352 1353 // #pragma mark - 1354 1355 1356 ServerBitmap* 1357 DrawingEngine::DumpToBitmap() 1358 { 1359 return NULL; 1360 } 1361 1362 1363 status_t 1364 DrawingEngine::ReadBitmap(ServerBitmap* bitmap, bool drawCursor, BRect bounds) 1365 { 1366 ASSERT_EXCLUSIVE_LOCKED(); 1367 1368 RenderingBuffer* buffer = fGraphicsCard->FrontBuffer(); 1369 if (buffer == NULL) 1370 return B_ERROR; 1371 1372 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1373 bounds = bounds & clip; 1374 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 1375 1376 status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(), 1377 buffer->BytesPerRow(), buffer->ColorSpace(), 1378 bounds.LeftTop(), BPoint(0, 0), 1379 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1); 1380 1381 if (drawCursor) { 1382 ServerCursorReference cursorRef = fGraphicsCard->Cursor(); 1383 ServerCursor* cursor = cursorRef.Cursor(); 1384 if (!cursor) 1385 return result; 1386 int32 cursorWidth = cursor->Width(); 1387 int32 cursorHeight = cursor->Height(); 1388 1389 BPoint cursorPosition = fGraphicsCard->CursorPosition(); 1390 cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot(); 1391 1392 BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1), 1393 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1394 1395 cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(), 1396 bitmap->BytesPerRow(), bitmap->ColorSpace(), 1397 cursorPosition, BPoint(0, 0), 1398 cursorWidth, cursorHeight); 1399 1400 uint8* bits = (uint8*)cursorArea.Bits(); 1401 uint8* cursorBits = (uint8*)cursor->Bits(); 1402 for (int32 i = 0; i < cursorHeight; i++) { 1403 for (int32 j = 0; j < cursorWidth; j++) { 1404 uint8 alpha = 255 - cursorBits[3]; 1405 bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0]; 1406 bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1]; 1407 bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2]; 1408 cursorBits += 4; 1409 bits += 4; 1410 } 1411 } 1412 1413 bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(), 1414 cursorArea.BytesPerRow(), cursorArea.ColorSpace(), 1415 BPoint(0, 0), cursorPosition, 1416 cursorWidth, cursorHeight); 1417 } 1418 1419 return result; 1420 } 1421 1422 1423 // #pragma mark - 1424 1425 1426 BRect 1427 DrawingEngine::CopyRect(BRect src, int32 xOffset, int32 yOffset) const 1428 { 1429 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1430 BRect dst; 1431 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1432 if (buffer) { 1433 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1434 1435 dst = src; 1436 dst.OffsetBy(xOffset, yOffset); 1437 1438 if (clip.Intersects(src) && clip.Intersects(dst)) { 1439 uint32 bytesPerRow = buffer->BytesPerRow(); 1440 uint8* bits = (uint8*)buffer->Bits(); 1441 1442 // clip source rect 1443 src = src & clip; 1444 // clip dest rect 1445 dst = dst & clip; 1446 // move dest back over source and clip source to dest 1447 dst.OffsetBy(-xOffset, -yOffset); 1448 src = src & dst; 1449 1450 // calc offset in buffer 1451 bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow; 1452 1453 uint32 width = src.IntegerWidth() + 1; 1454 uint32 height = src.IntegerHeight() + 1; 1455 1456 _CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset); 1457 1458 // offset dest again, because it is return value 1459 dst.OffsetBy(xOffset, yOffset); 1460 } 1461 } 1462 return dst; 1463 } 1464 1465 1466 void 1467 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, 1468 uint32 bytesPerRow, int32 xOffset, int32 yOffset) const 1469 { 1470 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1471 int32 xIncrement; 1472 int32 yIncrement; 1473 1474 if (yOffset == 0 && xOffset > 0) { 1475 // copy from right to left 1476 xIncrement = -1; 1477 src += (width - 1) * 4; 1478 } else { 1479 // copy from left to right 1480 xIncrement = 1; 1481 } 1482 1483 if (yOffset > 0) { 1484 // copy from bottom to top 1485 yIncrement = -bytesPerRow; 1486 src += (height - 1) * bytesPerRow; 1487 } else { 1488 // copy from top to bottom 1489 yIncrement = bytesPerRow; 1490 } 1491 1492 uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; 1493 1494 if (xIncrement == 1) { 1495 uint8 tmpBuffer[width * 4]; 1496 for (uint32 y = 0; y < height; y++) { 1497 // NOTE: read into temporary scanline buffer, 1498 // avoid memcpy because it might be graphics card memory 1499 gfxcpy32(tmpBuffer, src, width * 4); 1500 // write back temporary scanline buffer 1501 // NOTE: **don't read and write over the PCI bus 1502 // at the same time** 1503 memcpy(dst, tmpBuffer, width * 4); 1504 // NOTE: this (instead of the two pass copy above) might 1505 // speed up QEMU -> ?!? (would depend on how it emulates 1506 // the PCI bus...) 1507 // TODO: would be nice if we actually knew 1508 // if we're operating in graphics memory or main memory... 1509 //memcpy(dst, src, width * 4); 1510 src += yIncrement; 1511 dst += yIncrement; 1512 } 1513 } else { 1514 for (uint32 y = 0; y < height; y++) { 1515 uint32* srcHandle = (uint32*)src; 1516 uint32* dstHandle = (uint32*)dst; 1517 for (uint32 x = 0; x < width; x++) { 1518 *dstHandle = *srcHandle; 1519 srcHandle += xIncrement; 1520 dstHandle += xIncrement; 1521 } 1522 src += yIncrement; 1523 dst += yIncrement; 1524 } 1525 } 1526 } 1527 1528 1529 inline void 1530 DrawingEngine::_CopyToFront(const BRect& frame) 1531 { 1532 if (fCopyToFront) 1533 fGraphicsCard->Invalidate(frame); 1534 } 1535