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