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->FillArc(center, xRadius, yRadius, angle, span, gradient); 643 644 _CopyToFront(clipped); 645 } 646 } 647 648 649 void 650 DrawingEngine::DrawBezier(BPoint* pts, bool filled) 651 { 652 CRASH_IF_NOT_LOCKED 653 654 // TODO: figure out bounds and hide cursor depending on that 655 AutoFloatingOverlaysHider _(fGraphicsCard); 656 657 BRect touched = fPainter->DrawBezier(pts, filled); 658 659 _CopyToFront(touched); 660 } 661 662 663 void 664 DrawingEngine::FillBezier(BPoint* pts, const BGradient& gradient) 665 { 666 CRASH_IF_NOT_LOCKED 667 668 // TODO: figure out bounds and hide cursor depending on that 669 AutoFloatingOverlaysHider _(fGraphicsCard); 670 671 BRect touched = fPainter->FillBezier(pts, gradient); 672 673 _CopyToFront(touched); 674 } 675 676 677 void 678 DrawingEngine::DrawEllipse(BRect r, bool filled) 679 { 680 CRASH_IF_NOT_LOCKED 681 682 make_rect_valid(r); 683 BRect clipped = r; 684 fPainter->AlignEllipseRect(&clipped, filled); 685 686 if (!filled) 687 extend_by_stroke_width(clipped, fPainter->PenSize()); 688 689 clipped.left = floorf(clipped.left); 690 clipped.top = floorf(clipped.top); 691 clipped.right = ceilf(clipped.right); 692 clipped.bottom = ceilf(clipped.bottom); 693 694 clipped = fPainter->ClipRect(clipped); 695 696 if (clipped.IsValid()) { 697 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 698 699 fPainter->DrawEllipse(r, filled); 700 701 _CopyToFront(clipped); 702 } 703 } 704 705 706 void 707 DrawingEngine::FillEllipse(BRect r, const BGradient& gradient) 708 { 709 CRASH_IF_NOT_LOCKED 710 711 make_rect_valid(r); 712 BRect clipped = r; 713 fPainter->AlignEllipseRect(&clipped, true); 714 715 clipped.left = floorf(clipped.left); 716 clipped.top = floorf(clipped.top); 717 clipped.right = ceilf(clipped.right); 718 clipped.bottom = ceilf(clipped.bottom); 719 720 clipped = fPainter->ClipRect(clipped); 721 722 if (clipped.IsValid()) { 723 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 724 725 fPainter->FillEllipse(r, gradient); 726 727 _CopyToFront(clipped); 728 } 729 } 730 731 732 void 733 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, BRect bounds, 734 bool filled, bool closed) 735 { 736 CRASH_IF_NOT_LOCKED 737 738 make_rect_valid(bounds); 739 if (!filled) 740 extend_by_stroke_width(bounds, fPainter->PenSize()); 741 bounds = fPainter->ClipRect(bounds); 742 if (bounds.IsValid()) { 743 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 744 745 fPainter->DrawPolygon(ptlist, numpts, filled, closed); 746 747 _CopyToFront(bounds); 748 } 749 } 750 751 752 void 753 DrawingEngine::FillPolygon(BPoint* ptlist, int32 numpts, BRect bounds, 754 const BGradient& gradient, bool closed) 755 { 756 CRASH_IF_NOT_LOCKED 757 758 make_rect_valid(bounds); 759 bounds = fPainter->ClipRect(bounds); 760 if (bounds.IsValid()) { 761 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 762 763 fPainter->FillPolygon(ptlist, numpts, gradient, closed); 764 765 _CopyToFront(bounds); 766 } 767 } 768 769 770 // #pragma mark - rgb_color 771 772 773 void 774 DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color& color) 775 { 776 StrokeLine(pt, pt, color); 777 } 778 779 // StrokeLine 780 // 781 // * this function is only used by Decorators 782 // * it assumes a one pixel wide line 783 void 784 DrawingEngine::StrokeLine(const BPoint& start, const BPoint& end, 785 const rgb_color& color) 786 { 787 CRASH_IF_NOT_LOCKED 788 789 BRect touched(start, end); 790 make_rect_valid(touched); 791 touched = fPainter->ClipRect(touched); 792 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 793 794 if (!fPainter->StraightLine(start, end, color)) { 795 rgb_color previousColor = fPainter->HighColor(); 796 drawing_mode previousMode = fPainter->DrawingMode(); 797 798 fPainter->SetHighColor(color); 799 fPainter->SetDrawingMode(B_OP_OVER); 800 fPainter->StrokeLine(start, end); 801 802 fPainter->SetDrawingMode(previousMode); 803 fPainter->SetHighColor(previousColor); 804 } 805 806 _CopyToFront(touched); 807 } 808 809 // this function is used to draw a one pixel wide rect 810 void 811 DrawingEngine::StrokeRect(BRect r, const rgb_color &color) 812 { 813 CRASH_IF_NOT_LOCKED 814 815 make_rect_valid(r); 816 BRect clipped = fPainter->ClipRect(r); 817 if (clipped.IsValid()) { 818 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 819 820 fPainter->StrokeRect(r, color); 821 822 _CopyToFront(clipped); 823 } 824 } 825 826 827 void 828 DrawingEngine::FillRect(BRect r, const rgb_color& color) 829 { 830 CRASH_IF_NOT_LOCKED 831 832 // NOTE: Write locking because we might use HW acceleration. 833 // This needs to be investigated, I'm doing this because of 834 // gut feeling. 835 make_rect_valid(r); 836 r = fPainter->ClipRect(r); 837 if (!r.IsValid()) 838 return; 839 840 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 841 842 // try hardware optimized version first 843 if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { 844 BRegion region(r); 845 region.IntersectWith(fPainter->ClippingRegion()); 846 fGraphicsCard->FillRegion(region, color, 847 fSuspendSyncLevel == 0 || overlaysHider.WasHidden()); 848 } else { 849 fPainter->FillRect(r, color); 850 } 851 852 _CopyToFront(r); 853 } 854 855 856 void 857 DrawingEngine::FillRegion(BRegion& r, const rgb_color& color) 858 { 859 CRASH_IF_NOT_LOCKED 860 861 // NOTE: region expected to be already clipped correctly!! 862 BRect frame = r.Frame(); 863 if (!fPainter->Bounds().Contains(frame)) { 864 // NOTE: I am not quite sure yet how this can happen, but appearantly it can (see bug 634) 865 // This function is used for internal app_server painting, in the case of bug 634, 866 // the background of views is painted. But the view region should never be outside the 867 // frame buffer bounds. 868 // char message[1024]; 869 // BRect bounds = fPainter->Bounds(); 870 // sprintf(message, "FillRegion() - painter: (%d, %d)->(%d, %d), region: (%d, %d)->(%d, %d)", 871 // (int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom, 872 // (int)frame.left, (int)frame.top, (int)frame.right, (int)frame.bottom); 873 // debugger(message); 874 return; 875 } 876 877 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, frame); 878 879 // try hardware optimized version first 880 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 881 && frame.Width() * frame.Height() > 100) { 882 fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 883 || overlaysHider.WasHidden()); 884 } else { 885 int32 count = r.CountRects(); 886 for (int32 i = 0; i < count; i++) 887 fPainter->FillRectNoClipping(r.RectAtInt(i), color); 888 } 889 890 _CopyToFront(frame); 891 } 892 893 // #pragma mark - DrawState 894 895 void 896 DrawingEngine::StrokeRect(BRect r) 897 { 898 CRASH_IF_NOT_LOCKED 899 900 // support invalid rects 901 make_rect_valid(r); 902 BRect clipped(r); 903 extend_by_stroke_width(clipped, fPainter->PenSize()); 904 clipped = fPainter->ClipRect(clipped); 905 if (clipped.IsValid()) { 906 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 907 908 fPainter->StrokeRect(r); 909 910 _CopyToFront(clipped); 911 } 912 } 913 914 915 void 916 DrawingEngine::FillRect(BRect r) 917 { 918 CRASH_IF_NOT_LOCKED 919 920 make_rect_valid(r); 921 r = fPainter->AlignAndClipRect(r); 922 if (!r.IsValid()) 923 return; 924 925 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 926 927 bool doInSoftware = true; 928 if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { 929 // try hardware optimized version first 930 // if the rect is large enough 931 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 932 if (fPainter->Pattern() == B_SOLID_HIGH 933 && (fPainter->DrawingMode() == B_OP_COPY 934 || fPainter->DrawingMode() == B_OP_OVER)) { 935 BRegion region(r); 936 region.IntersectWith(fPainter->ClippingRegion()); 937 fGraphicsCard->FillRegion(region, fPainter->HighColor(), 938 fSuspendSyncLevel == 0 939 || overlaysHider.WasHidden()); 940 doInSoftware = false; 941 } else if (fPainter->Pattern() == B_SOLID_LOW 942 && fPainter->DrawingMode() == B_OP_COPY) { 943 BRegion region(r); 944 region.IntersectWith(fPainter->ClippingRegion()); 945 fGraphicsCard->FillRegion(region, fPainter->LowColor(), 946 fSuspendSyncLevel == 0 947 || overlaysHider.WasHidden()); 948 doInSoftware = false; 949 } 950 } 951 } 952 953 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 954 && fPainter->Pattern() == B_SOLID_HIGH 955 && fPainter->DrawingMode() == B_OP_INVERT) { 956 BRegion region(r); 957 region.IntersectWith(fPainter->ClippingRegion()); 958 fGraphicsCard->InvertRegion(region); 959 doInSoftware = false; 960 } 961 962 if (doInSoftware) 963 fPainter->FillRect(r); 964 965 _CopyToFront(r); 966 } 967 968 969 void 970 DrawingEngine::FillRect(BRect r, const BGradient& gradient) 971 { 972 CRASH_IF_NOT_LOCKED 973 974 make_rect_valid(r); 975 r = fPainter->AlignAndClipRect(r); 976 if (!r.IsValid()) 977 return; 978 979 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 980 981 fPainter->FillRect(r, gradient); 982 983 _CopyToFront(r); 984 } 985 986 987 void 988 DrawingEngine::FillRegion(BRegion& r) 989 { 990 CRASH_IF_NOT_LOCKED 991 992 BRect clipped = fPainter->ClipRect(r.Frame()); 993 if (!clipped.IsValid()) 994 return; 995 996 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped); 997 998 bool doInSoftware = true; 999 // try hardware optimized version first 1000 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 1001 if (fPainter->Pattern() == B_SOLID_HIGH 1002 && (fPainter->DrawingMode() == B_OP_COPY 1003 || fPainter->DrawingMode() == B_OP_OVER)) { 1004 r.IntersectWith(fPainter->ClippingRegion()); 1005 fGraphicsCard->FillRegion(r, fPainter->HighColor(), 1006 fSuspendSyncLevel == 0 1007 || overlaysHider.WasHidden()); 1008 doInSoftware = false; 1009 } else if (fPainter->Pattern() == B_SOLID_LOW 1010 && fPainter->DrawingMode() == B_OP_COPY) { 1011 r.IntersectWith(fPainter->ClippingRegion()); 1012 fGraphicsCard->FillRegion(r, fPainter->LowColor(), 1013 fSuspendSyncLevel == 0 1014 || overlaysHider.WasHidden()); 1015 doInSoftware = false; 1016 } 1017 } 1018 1019 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 1020 && fPainter->Pattern() == B_SOLID_HIGH 1021 && fPainter->DrawingMode() == B_OP_INVERT) { 1022 r.IntersectWith(fPainter->ClippingRegion()); 1023 fGraphicsCard->InvertRegion(r); 1024 doInSoftware = false; 1025 } 1026 1027 if (doInSoftware) { 1028 1029 BRect touched = fPainter->FillRect(r.RectAt(0)); 1030 1031 int32 count = r.CountRects(); 1032 for (int32 i = 1; i < count; i++) 1033 touched = touched | fPainter->FillRect(r.RectAt(i)); 1034 } 1035 1036 _CopyToFront(r.Frame()); 1037 } 1038 1039 1040 void 1041 DrawingEngine::FillRegion(BRegion& r, const BGradient& gradient) 1042 { 1043 CRASH_IF_NOT_LOCKED 1044 1045 BRect clipped = fPainter->ClipRect(r.Frame()); 1046 if (!clipped.IsValid()) 1047 return; 1048 1049 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped); 1050 1051 BRect touched = fPainter->FillRect(r.RectAt(0), gradient); 1052 1053 int32 count = r.CountRects(); 1054 for (int32 i = 1; i < count; i++) 1055 touched = touched | fPainter->FillRect(r.RectAt(i), gradient); 1056 1057 _CopyToFront(r.Frame()); 1058 } 1059 1060 1061 void 1062 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled) 1063 { 1064 CRASH_IF_NOT_LOCKED 1065 1066 // NOTE: the stroke does not extend past "r" in R5, 1067 // though I consider this unexpected behaviour. 1068 make_rect_valid(r); 1069 BRect clipped = fPainter->ClipRect(r); 1070 1071 clipped.left = floorf(clipped.left); 1072 clipped.top = floorf(clipped.top); 1073 clipped.right = ceilf(clipped.right); 1074 clipped.bottom = ceilf(clipped.bottom); 1075 1076 if (clipped.IsValid()) { 1077 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1078 1079 BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad) 1080 : fPainter->StrokeRoundRect(r, xrad, yrad); 1081 1082 _CopyToFront(touched); 1083 } 1084 } 1085 1086 1087 void 1088 DrawingEngine::FillRoundRect(BRect r, float xrad, float yrad, 1089 const BGradient& gradient) 1090 { 1091 CRASH_IF_NOT_LOCKED 1092 1093 // NOTE: the stroke does not extend past "r" in R5, 1094 // though I consider this unexpected behaviour. 1095 make_rect_valid(r); 1096 BRect clipped = fPainter->ClipRect(r); 1097 1098 clipped.left = floorf(clipped.left); 1099 clipped.top = floorf(clipped.top); 1100 clipped.right = ceilf(clipped.right); 1101 clipped.bottom = ceilf(clipped.bottom); 1102 1103 if (clipped.IsValid()) { 1104 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1105 1106 BRect touched = fPainter->FillRoundRect(r, xrad, yrad, gradient); 1107 1108 _CopyToFront(touched); 1109 } 1110 } 1111 1112 1113 void 1114 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 1115 const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled) 1116 { 1117 CRASH_IF_NOT_LOCKED 1118 1119 // NOTE: hides cursor regardless of if and where 1120 // shape is drawn on screen, TODO: optimize 1121 AutoFloatingOverlaysHider _(fGraphicsCard); 1122 1123 BRect touched = fPainter->DrawShape(opCount, opList, 1124 ptCount, ptList, 1125 filled); 1126 1127 _CopyToFront(touched); 1128 } 1129 1130 1131 void 1132 DrawingEngine::FillShape(const BRect& bounds, int32 opCount, 1133 const uint32* opList, int32 ptCount, const BPoint* ptList, 1134 const BGradient& gradient) 1135 { 1136 CRASH_IF_NOT_LOCKED 1137 1138 // NOTE: hides cursor regardless of if and where 1139 // shape is drawn on screen, TODO: optimize 1140 AutoFloatingOverlaysHider _(fGraphicsCard); 1141 1142 BRect touched = fPainter->FillShape(opCount, opList, ptCount, 1143 ptList, gradient); 1144 1145 _CopyToFront(touched); 1146 } 1147 1148 1149 void 1150 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled) 1151 { 1152 CRASH_IF_NOT_LOCKED 1153 1154 BRect clipped(bounds); 1155 if (!filled) 1156 extend_by_stroke_width(clipped, fPainter->PenSize()); 1157 clipped = fPainter->ClipRect(clipped); 1158 if (clipped.IsValid()) { 1159 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1160 1161 if (filled) 1162 fPainter->FillTriangle(pts[0], pts[1], pts[2]); 1163 else 1164 fPainter->StrokeTriangle(pts[0], pts[1], pts[2]); 1165 1166 _CopyToFront(clipped); 1167 } 1168 } 1169 1170 void 1171 DrawingEngine::FillTriangle(BPoint* pts, const BRect& bounds, 1172 const BGradient& gradient) 1173 { 1174 CRASH_IF_NOT_LOCKED 1175 1176 BRect clipped(bounds); 1177 clipped = fPainter->ClipRect(clipped); 1178 if (clipped.IsValid()) { 1179 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 1180 1181 fPainter->FillTriangle(pts[0], pts[1], pts[2], gradient); 1182 1183 _CopyToFront(clipped); 1184 } 1185 } 1186 1187 // StrokeLine 1188 void 1189 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) 1190 { 1191 CRASH_IF_NOT_LOCKED 1192 1193 BRect touched(start, end); 1194 make_rect_valid(touched); 1195 extend_by_stroke_width(touched, fPainter->PenSize()); 1196 touched = fPainter->ClipRect(touched); 1197 if (touched.IsValid()) { 1198 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 1199 1200 fPainter->StrokeLine(start, end); 1201 1202 _CopyToFront(touched); 1203 } 1204 } 1205 1206 // StrokeLineArray 1207 void 1208 DrawingEngine::StrokeLineArray(int32 numLines, 1209 const LineArrayData *linedata) 1210 { 1211 CRASH_IF_NOT_LOCKED 1212 1213 if (!linedata || numLines <= 0) 1214 return; 1215 1216 // figure out bounding box for line array 1217 const LineArrayData *data = (const LineArrayData *)&(linedata[0]); 1218 BRect touched(min_c(data->pt1.x, data->pt2.x), 1219 min_c(data->pt1.y, data->pt2.y), 1220 max_c(data->pt1.x, data->pt2.x), 1221 max_c(data->pt1.y, data->pt2.y)); 1222 1223 for (int32 i = 1; i < numLines; i++) { 1224 data = (const LineArrayData *)&(linedata[i]); 1225 BRect box(min_c(data->pt1.x, data->pt2.x), 1226 min_c(data->pt1.y, data->pt2.y), 1227 max_c(data->pt1.x, data->pt2.x), 1228 max_c(data->pt1.y, data->pt2.y)); 1229 touched = touched | box; 1230 } 1231 extend_by_stroke_width(touched, fPainter->PenSize()); 1232 touched = fPainter->ClipRect(touched); 1233 if (touched.IsValid()) { 1234 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 1235 1236 data = (const LineArrayData *)&(linedata[0]); 1237 1238 // store current graphics state, we mess with the 1239 // high color and pattern... 1240 rgb_color oldColor = fPainter->HighColor(); 1241 struct pattern pattern = fPainter->Pattern(); 1242 1243 fPainter->SetHighColor(data->color); 1244 fPainter->SetPattern(B_SOLID_HIGH); 1245 fPainter->StrokeLine(data->pt1, data->pt2); 1246 1247 for (int32 i = 1; i < numLines; i++) { 1248 data = (const LineArrayData *)&(linedata[i]); 1249 fPainter->SetHighColor(data->color); 1250 fPainter->StrokeLine(data->pt1, data->pt2); 1251 } 1252 1253 // restore correct drawing state highcolor and pattern 1254 fPainter->SetHighColor(oldColor); 1255 fPainter->SetPattern(pattern); 1256 1257 _CopyToFront(touched); 1258 } 1259 } 1260 1261 // #pragma mark - 1262 1263 BPoint 1264 DrawingEngine::DrawString(const char* string, int32 length, 1265 const BPoint& pt, escapement_delta* delta) 1266 { 1267 CRASH_IF_NOT_LOCKED 1268 1269 BPoint penLocation = pt; 1270 1271 // try a fast clipping path 1272 if (fPainter->ClippingRegion() && fPainter->Font().Rotation() == 0.0f) { 1273 float fontSize = fPainter->Font().Size(); 1274 BRect clippingFrame = fPainter->ClippingRegion()->Frame(); 1275 if (pt.x - fontSize > clippingFrame.right 1276 || pt.y + fontSize < clippingFrame.top 1277 || pt.y - fontSize > clippingFrame.bottom) { 1278 penLocation.x += StringWidth(string, length, delta); 1279 return penLocation; 1280 } 1281 } 1282 1283 // use a FontCacheRefernece to speed up the second pass of 1284 // drawing the string 1285 FontCacheReference cacheReference; 1286 1287 //bigtime_t now = system_time(); 1288 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. 1289 // Cursiously, the DrawString after it is actually faster!?! 1290 // TODO: make the availability of the hardware cursor part of the 1291 // HW acceleration flags and skip all calculations for HideFloatingOverlays 1292 // in case we don't have one. 1293 // TODO: Watch out about penLocation and use Painter::PenLocation() when 1294 // not using BoundindBox anymore. 1295 BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta, 1296 &cacheReference); 1297 // stop here if we're supposed to render outside of the clipping 1298 b = fPainter->ClipRect(b); 1299 if (b.IsValid()) { 1300 //printf("bounding box '%s': %lld µs\n", string, system_time() - now); 1301 AutoFloatingOverlaysHider _(fGraphicsCard, b); 1302 1303 //now = system_time(); 1304 BRect touched = fPainter->DrawString(string, length, pt, delta, 1305 &cacheReference); 1306 //printf("drawing string: %lld µs\n", system_time() - now); 1307 1308 _CopyToFront(touched); 1309 } 1310 1311 return penLocation; 1312 } 1313 1314 // StringWidth 1315 float 1316 DrawingEngine::StringWidth(const char* string, int32 length, 1317 escapement_delta* delta) 1318 { 1319 return fPainter->StringWidth(string, length, delta); 1320 } 1321 1322 // StringWidth 1323 float 1324 DrawingEngine::StringWidth(const char* string, int32 length, 1325 const ServerFont& font, escapement_delta* delta) 1326 { 1327 return font.StringWidth(string, length, delta); 1328 } 1329 1330 // #pragma mark - 1331 1332 // DumpToBitmap 1333 ServerBitmap* 1334 DrawingEngine::DumpToBitmap() 1335 { 1336 return NULL; 1337 } 1338 1339 status_t 1340 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds) 1341 { 1342 CRASH_IF_NOT_EXCLUSIVE_LOCKED 1343 1344 RenderingBuffer *buffer = fGraphicsCard->FrontBuffer(); 1345 if (!buffer) 1346 return B_ERROR; 1347 1348 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1349 bounds = bounds & clip; 1350 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 1351 1352 status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(), 1353 buffer->BytesPerRow(), buffer->ColorSpace(), 1354 bounds.LeftTop(), BPoint(0, 0), 1355 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1); 1356 1357 if (drawCursor) { 1358 ServerCursorReference cursorRef = fGraphicsCard->Cursor(); 1359 ServerCursor* cursor = cursorRef.Cursor(); 1360 if (!cursor) 1361 return result; 1362 int32 cursorWidth = cursor->Width(); 1363 int32 cursorHeight = cursor->Height(); 1364 1365 BPoint cursorPosition = fGraphicsCard->CursorPosition(); 1366 cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot(); 1367 1368 BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1), 1369 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1370 1371 cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(), 1372 bitmap->BytesPerRow(), bitmap->ColorSpace(), 1373 cursorPosition, BPoint(0, 0), 1374 cursorWidth, cursorHeight); 1375 1376 uint8 *bits = (uint8 *)cursorArea.Bits(); 1377 uint8 *cursorBits = (uint8 *)cursor->Bits(); 1378 for (int32 i = 0; i < cursorHeight; i++) { 1379 for (int32 j = 0; j < cursorWidth; j++) { 1380 uint8 alpha = 255 - cursorBits[3]; 1381 bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0]; 1382 bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1]; 1383 bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2]; 1384 cursorBits += 4; 1385 bits += 4; 1386 } 1387 } 1388 1389 bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(), 1390 cursorArea.BytesPerRow(), cursorArea.ColorSpace(), 1391 BPoint(0, 0), cursorPosition, 1392 cursorWidth, cursorHeight); 1393 } 1394 1395 return result; 1396 } 1397 1398 // #pragma mark - 1399 1400 BRect 1401 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const 1402 { 1403 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1404 BRect dst; 1405 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1406 if (buffer) { 1407 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1408 1409 dst = src; 1410 dst.OffsetBy(xOffset, yOffset); 1411 1412 if (clip.Intersects(src) && clip.Intersects(dst)) { 1413 uint32 bytesPerRow = buffer->BytesPerRow(); 1414 uint8* bits = (uint8*)buffer->Bits(); 1415 1416 // clip source rect 1417 src = src & clip; 1418 // clip dest rect 1419 dst = dst & clip; 1420 // move dest back over source and clip source to dest 1421 dst.OffsetBy(-xOffset, -yOffset); 1422 src = src & dst; 1423 1424 // calc offset in buffer 1425 bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow; 1426 1427 uint32 width = src.IntegerWidth() + 1; 1428 uint32 height = src.IntegerHeight() + 1; 1429 1430 _CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset); 1431 1432 // offset dest again, because it is return value 1433 dst.OffsetBy(xOffset, yOffset); 1434 } 1435 } 1436 return dst; 1437 } 1438 1439 1440 void 1441 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, 1442 uint32 bytesPerRow, int32 xOffset, int32 yOffset) const 1443 { 1444 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1445 int32 xIncrement; 1446 int32 yIncrement; 1447 1448 if (yOffset == 0 && xOffset > 0) { 1449 // copy from right to left 1450 xIncrement = -1; 1451 src += (width - 1) * 4; 1452 } else { 1453 // copy from left to right 1454 xIncrement = 1; 1455 } 1456 1457 if (yOffset > 0) { 1458 // copy from bottom to top 1459 yIncrement = -bytesPerRow; 1460 src += (height - 1) * bytesPerRow; 1461 } else { 1462 // copy from top to bottom 1463 yIncrement = bytesPerRow; 1464 } 1465 1466 uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; 1467 1468 if (xIncrement == 1) { 1469 uint8 tmpBuffer[width * 4]; 1470 for (uint32 y = 0; y < height; y++) { 1471 // NOTE: read into temporary scanline buffer, 1472 // avoid memcpy because it might be graphics card memory 1473 gfxcpy32(tmpBuffer, src, width * 4); 1474 // write back temporary scanline buffer 1475 // NOTE: **don't read and write over the PCI bus 1476 // at the same time** 1477 memcpy(dst, tmpBuffer, width * 4); 1478 // NOTE: this (instead of the two pass copy above) might 1479 // speed up QEMU -> ?!? (would depend on how it emulates 1480 // the PCI bus...) 1481 // TODO: would be nice if we actually knew 1482 // if we're operating in graphics memory or main memory... 1483 //memcpy(dst, src, width * 4); 1484 src += yIncrement; 1485 dst += yIncrement; 1486 } 1487 } else { 1488 for (uint32 y = 0; y < height; y++) { 1489 uint32* srcHandle = (uint32*)src; 1490 uint32* dstHandle = (uint32*)dst; 1491 for (uint32 x = 0; x < width; x++) { 1492 *dstHandle = *srcHandle; 1493 srcHandle += xIncrement; 1494 dstHandle += xIncrement; 1495 } 1496 src += yIncrement; 1497 dst += yIncrement; 1498 } 1499 } 1500 } 1501 1502 1503 inline void 1504 DrawingEngine::_CopyToFront(const BRect& frame) 1505 { 1506 if (fCopyToFront) 1507 fGraphicsCard->Invalidate(frame); 1508 } 1509 1510 1511