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