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 532 delete[] sortedRectList; 533 } 534 535 // InvertRect 536 void 537 DrawingEngine::InvertRect(BRect r) 538 { 539 CRASH_IF_NOT_LOCKED 540 541 make_rect_valid(r); 542 r = fPainter->ClipRect(r); 543 if (r.IsValid()) { 544 AutoFloatingOverlaysHider _(fGraphicsCard, r); 545 546 // try hardware optimized version first 547 if (fAvailableHWAccleration & HW_ACC_INVERT_REGION) { 548 BRegion region(r); 549 region.IntersectWith(fPainter->ClippingRegion()); 550 fGraphicsCard->InvertRegion(region); 551 } else { 552 fPainter->InvertRect(r); 553 554 _CopyToFront(r); 555 } 556 } 557 } 558 559 // DrawBitmap 560 void 561 DrawingEngine::DrawBitmap(ServerBitmap *bitmap, 562 const BRect &source, const BRect &dest) 563 { 564 CRASH_IF_NOT_LOCKED 565 566 BRect clipped = fPainter->ClipRect(dest); 567 if (clipped.IsValid()) { 568 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 569 570 fPainter->DrawBitmap(bitmap, source, dest); 571 572 _CopyToFront(clipped); 573 } 574 } 575 576 // DrawArc 577 void 578 DrawingEngine::DrawArc(BRect r, const float &angle, 579 const float &span, bool filled) 580 { 581 CRASH_IF_NOT_LOCKED 582 583 make_rect_valid(r); 584 BRect clipped(r); 585 if (!filled) 586 extend_by_stroke_width(clipped, fPainter->PenSize()); 587 clipped = fPainter->ClipRect(r); 588 if (clipped.IsValid()) { 589 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 590 591 float xRadius = r.Width() / 2.0; 592 float yRadius = r.Height() / 2.0; 593 BPoint center(r.left + xRadius, 594 r.top + yRadius); 595 596 if (filled) 597 fPainter->FillArc(center, xRadius, yRadius, angle, span); 598 else 599 fPainter->StrokeArc(center, xRadius, yRadius, angle, span); 600 601 _CopyToFront(clipped); 602 } 603 } 604 605 // DrawBezier 606 void 607 DrawingEngine::DrawBezier(BPoint *pts, bool filled) 608 { 609 CRASH_IF_NOT_LOCKED 610 611 // TODO: figure out bounds and hide cursor depending on that 612 AutoFloatingOverlaysHider _(fGraphicsCard); 613 614 BRect touched = fPainter->DrawBezier(pts, filled); 615 616 _CopyToFront(touched); 617 } 618 619 // DrawEllipse 620 void 621 DrawingEngine::DrawEllipse(BRect r, bool filled) 622 { 623 CRASH_IF_NOT_LOCKED 624 625 make_rect_valid(r); 626 BRect clipped = r; 627 fPainter->AlignEllipseRect(&clipped, filled); 628 629 if (!filled) 630 extend_by_stroke_width(clipped, fPainter->PenSize()); 631 632 clipped.left = floorf(clipped.left); 633 clipped.top = floorf(clipped.top); 634 clipped.right = ceilf(clipped.right); 635 clipped.bottom = ceilf(clipped.bottom); 636 637 clipped = fPainter->ClipRect(clipped); 638 639 if (clipped.IsValid()) { 640 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 641 642 fPainter->DrawEllipse(r, filled); 643 644 _CopyToFront(clipped); 645 } 646 } 647 648 // DrawPolygon 649 void 650 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, 651 BRect bounds, bool filled, bool closed) 652 { 653 CRASH_IF_NOT_LOCKED 654 655 make_rect_valid(bounds); 656 if (!filled) 657 extend_by_stroke_width(bounds, fPainter->PenSize()); 658 bounds = fPainter->ClipRect(bounds); 659 if (bounds.IsValid()) { 660 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 661 662 fPainter->DrawPolygon(ptlist, numpts, filled, closed); 663 664 _CopyToFront(bounds); 665 } 666 } 667 668 // #pragma mark - rgb_color 669 670 void 671 DrawingEngine::StrokePoint(const BPoint& pt, const rgb_color &color) 672 { 673 StrokeLine(pt, pt, color); 674 } 675 676 // StrokeLine 677 // 678 // * this function is only used by Decorators 679 // * it assumes a one pixel wide line 680 void 681 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, 682 const rgb_color &color) 683 { 684 CRASH_IF_NOT_LOCKED 685 686 BRect touched(start, end); 687 make_rect_valid(touched); 688 touched = fPainter->ClipRect(touched); 689 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 690 691 if (!fPainter->StraightLine(start, end, color)) { 692 rgb_color previousColor = fPainter->HighColor(); 693 drawing_mode previousMode = fPainter->DrawingMode(); 694 695 fPainter->SetHighColor(color); 696 fPainter->SetDrawingMode(B_OP_OVER); 697 fPainter->StrokeLine(start, end); 698 699 fPainter->SetDrawingMode(previousMode); 700 fPainter->SetHighColor(previousColor); 701 } 702 703 _CopyToFront(touched); 704 } 705 706 // this function is used to draw a one pixel wide rect 707 void 708 DrawingEngine::StrokeRect(BRect r, const rgb_color &color) 709 { 710 CRASH_IF_NOT_LOCKED 711 712 make_rect_valid(r); 713 BRect clipped = fPainter->ClipRect(r); 714 if (clipped.IsValid()) { 715 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 716 717 fPainter->StrokeRect(r, color); 718 719 _CopyToFront(clipped); 720 } 721 } 722 723 724 void 725 DrawingEngine::FillRect(BRect r, const rgb_color& color) 726 { 727 CRASH_IF_NOT_LOCKED 728 729 // NOTE: Write locking because we might use HW acceleration. 730 // This needs to be investigated, I'm doing this because of 731 // gut feeling. 732 make_rect_valid(r); 733 r = fPainter->ClipRect(r); 734 if (r.IsValid()) { 735 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 736 737 // try hardware optimized version first 738 if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { 739 BRegion region(r); 740 region.IntersectWith(fPainter->ClippingRegion()); 741 fGraphicsCard->FillRegion(region, color, 742 fSuspendSyncLevel == 0 743 || overlaysHider.WasHidden()); 744 } else { 745 fPainter->FillRect(r, color); 746 747 _CopyToFront(r); 748 } 749 } 750 } 751 752 753 void 754 DrawingEngine::FillRegion(BRegion& r, const rgb_color& color) 755 { 756 CRASH_IF_NOT_LOCKED 757 758 // NOTE: region expected to be already clipped correctly!! 759 BRect frame = r.Frame(); 760 if (!fPainter->Bounds().Contains(frame)) { 761 // NOTE: I am not quite sure yet how this can happen, but appearantly it can (see bug 634) 762 // This function is used for internal app_server painting, in the case of bug 634, 763 // the background of views is painted. But the view region should never be outside the 764 // frame buffer bounds. 765 // char message[1024]; 766 // BRect bounds = fPainter->Bounds(); 767 // sprintf(message, "FillRegion() - painter: (%d, %d)->(%d, %d), region: (%d, %d)->(%d, %d)", 768 // (int)bounds.left, (int)bounds.top, (int)bounds.right, (int)bounds.bottom, 769 // (int)frame.left, (int)frame.top, (int)frame.right, (int)frame.bottom); 770 // debugger(message); 771 return; 772 } 773 774 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, frame); 775 776 // try hardware optimized version first 777 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 778 && frame.Width() * frame.Height() > 100) { 779 fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 780 || overlaysHider.WasHidden()); 781 } else { 782 int32 count = r.CountRects(); 783 for (int32 i = 0; i < count; i++) { 784 fPainter->FillRectNoClipping(r.RectAtInt(i), color); 785 } 786 787 _CopyToFront(frame); 788 } 789 } 790 791 // #pragma mark - DrawState 792 793 void 794 DrawingEngine::StrokeRect(BRect r) 795 { 796 CRASH_IF_NOT_LOCKED 797 798 // support invalid rects 799 make_rect_valid(r); 800 BRect clipped(r); 801 extend_by_stroke_width(clipped, fPainter->PenSize()); 802 clipped = fPainter->ClipRect(clipped); 803 if (clipped.IsValid()) { 804 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 805 806 fPainter->StrokeRect(r); 807 808 _CopyToFront(clipped); 809 } 810 } 811 812 813 void 814 DrawingEngine::FillRect(BRect r) 815 { 816 CRASH_IF_NOT_LOCKED 817 818 make_rect_valid(r); 819 r = fPainter->AlignAndClipRect(r); 820 if (r.IsValid()) { 821 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, r); 822 823 bool doInSoftware = true; 824 if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { 825 // try hardware optimized version first 826 // if the rect is large enough 827 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 828 if (fPainter->Pattern() == B_SOLID_HIGH 829 && (fPainter->DrawingMode() == B_OP_COPY 830 || fPainter->DrawingMode() == B_OP_OVER)) { 831 BRegion region(r); 832 region.IntersectWith(fPainter->ClippingRegion()); 833 fGraphicsCard->FillRegion(region, fPainter->HighColor(), 834 fSuspendSyncLevel == 0 835 || overlaysHider.WasHidden()); 836 doInSoftware = false; 837 } else if (fPainter->Pattern() == B_SOLID_LOW 838 && fPainter->DrawingMode() == B_OP_COPY) { 839 BRegion region(r); 840 region.IntersectWith(fPainter->ClippingRegion()); 841 fGraphicsCard->FillRegion(region, fPainter->LowColor(), 842 fSuspendSyncLevel == 0 843 || overlaysHider.WasHidden()); 844 doInSoftware = false; 845 } 846 } 847 } 848 849 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 850 && fPainter->Pattern() == B_SOLID_HIGH 851 && fPainter->DrawingMode() == B_OP_INVERT) { 852 BRegion region(r); 853 region.IntersectWith(fPainter->ClippingRegion()); 854 fGraphicsCard->InvertRegion(region); 855 doInSoftware = false; 856 } 857 858 if (doInSoftware) { 859 fPainter->FillRect(r); 860 861 _CopyToFront(r); 862 } 863 } 864 } 865 866 867 void 868 DrawingEngine::FillRegion(BRegion& r) 869 { 870 CRASH_IF_NOT_LOCKED 871 872 BRect clipped = fPainter->ClipRect(r.Frame()); 873 if (clipped.IsValid()) { 874 AutoFloatingOverlaysHider overlaysHider(fGraphicsCard, clipped); 875 876 bool doInSoftware = true; 877 // try hardware optimized version first 878 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 879 if (fPainter->Pattern() == B_SOLID_HIGH 880 && (fPainter->DrawingMode() == B_OP_COPY 881 || fPainter->DrawingMode() == B_OP_OVER)) { 882 r.IntersectWith(fPainter->ClippingRegion()); 883 fGraphicsCard->FillRegion(r, fPainter->HighColor(), 884 fSuspendSyncLevel == 0 885 || overlaysHider.WasHidden()); 886 doInSoftware = false; 887 } else if (fPainter->Pattern() == B_SOLID_LOW 888 && fPainter->DrawingMode() == B_OP_COPY) { 889 r.IntersectWith(fPainter->ClippingRegion()); 890 fGraphicsCard->FillRegion(r, fPainter->LowColor(), 891 fSuspendSyncLevel == 0 892 || overlaysHider.WasHidden()); 893 doInSoftware = false; 894 } 895 } 896 897 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 898 && fPainter->Pattern() == B_SOLID_HIGH 899 && fPainter->DrawingMode() == B_OP_INVERT) { 900 r.IntersectWith(fPainter->ClippingRegion()); 901 fGraphicsCard->InvertRegion(r); 902 doInSoftware = false; 903 } 904 905 if (doInSoftware) { 906 907 BRect touched = fPainter->FillRect(r.RectAt(0)); 908 909 int32 count = r.CountRects(); 910 for (int32 i = 1; i < count; i++) { 911 touched = touched | fPainter->FillRect(r.RectAt(i)); 912 } 913 914 _CopyToFront(touched); 915 } 916 } 917 } 918 919 920 void 921 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled) 922 { 923 CRASH_IF_NOT_LOCKED 924 925 // NOTE: the stroke does not extend past "r" in R5, 926 // though I consider this unexpected behaviour. 927 make_rect_valid(r); 928 BRect clipped = fPainter->ClipRect(r); 929 930 clipped.left = floorf(clipped.left); 931 clipped.top = floorf(clipped.top); 932 clipped.right = ceilf(clipped.right); 933 clipped.bottom = ceilf(clipped.bottom); 934 935 if (clipped.IsValid()) { 936 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 937 938 BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad) 939 : fPainter->StrokeRoundRect(r, xrad, yrad); 940 941 _CopyToFront(touched); 942 } 943 } 944 945 946 void 947 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 948 const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled) 949 { 950 CRASH_IF_NOT_LOCKED 951 952 // NOTE: hides cursor regardless of if and where 953 // shape is drawn on screen, TODO: optimize 954 AutoFloatingOverlaysHider _(fGraphicsCard); 955 956 BRect touched = fPainter->DrawShape(opCount, opList, 957 ptCount, ptList, 958 filled); 959 960 _CopyToFront(touched); 961 } 962 963 964 void 965 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled) 966 { 967 CRASH_IF_NOT_LOCKED 968 969 BRect clipped(bounds); 970 if (!filled) 971 extend_by_stroke_width(clipped, fPainter->PenSize()); 972 clipped = fPainter->ClipRect(clipped); 973 if (clipped.IsValid()) { 974 AutoFloatingOverlaysHider _(fGraphicsCard, clipped); 975 976 if (filled) 977 fPainter->FillTriangle(pts[0], pts[1], pts[2]); 978 else 979 fPainter->StrokeTriangle(pts[0], pts[1], pts[2]); 980 981 _CopyToFront(clipped); 982 } 983 } 984 985 // StrokeLine 986 void 987 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end) 988 { 989 CRASH_IF_NOT_LOCKED 990 991 BRect touched(start, end); 992 make_rect_valid(touched); 993 extend_by_stroke_width(touched, fPainter->PenSize()); 994 touched = fPainter->ClipRect(touched); 995 if (touched.IsValid()) { 996 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 997 998 fPainter->StrokeLine(start, end); 999 1000 _CopyToFront(touched); 1001 } 1002 } 1003 1004 // StrokeLineArray 1005 void 1006 DrawingEngine::StrokeLineArray(int32 numLines, 1007 const LineArrayData *linedata) 1008 { 1009 CRASH_IF_NOT_LOCKED 1010 1011 if (!linedata || numLines <= 0) 1012 return; 1013 1014 // figure out bounding box for line array 1015 const LineArrayData *data = (const LineArrayData *)&(linedata[0]); 1016 BRect touched(min_c(data->pt1.x, data->pt2.x), 1017 min_c(data->pt1.y, data->pt2.y), 1018 max_c(data->pt1.x, data->pt2.x), 1019 max_c(data->pt1.y, data->pt2.y)); 1020 1021 for (int32 i = 1; i < numLines; i++) { 1022 data = (const LineArrayData *)&(linedata[i]); 1023 BRect box(min_c(data->pt1.x, data->pt2.x), 1024 min_c(data->pt1.y, data->pt2.y), 1025 max_c(data->pt1.x, data->pt2.x), 1026 max_c(data->pt1.y, data->pt2.y)); 1027 touched = touched | box; 1028 } 1029 extend_by_stroke_width(touched, fPainter->PenSize()); 1030 touched = fPainter->ClipRect(touched); 1031 if (touched.IsValid()) { 1032 AutoFloatingOverlaysHider _(fGraphicsCard, touched); 1033 1034 data = (const LineArrayData *)&(linedata[0]); 1035 1036 // store current graphics state, we mess with the 1037 // high color and pattern... 1038 rgb_color oldColor = fPainter->HighColor(); 1039 struct pattern pattern = fPainter->Pattern(); 1040 1041 fPainter->SetHighColor(data->color); 1042 fPainter->SetPattern(B_SOLID_HIGH); 1043 fPainter->StrokeLine(data->pt1, data->pt2); 1044 1045 for (int32 i = 1; i < numLines; i++) { 1046 data = (const LineArrayData *)&(linedata[i]); 1047 fPainter->SetHighColor(data->color); 1048 fPainter->StrokeLine(data->pt1, data->pt2); 1049 } 1050 1051 // restore correct drawing state highcolor and pattern 1052 fPainter->SetHighColor(oldColor); 1053 fPainter->SetPattern(pattern); 1054 1055 _CopyToFront(touched); 1056 } 1057 } 1058 1059 // #pragma mark - 1060 1061 BPoint 1062 DrawingEngine::DrawString(const char* string, int32 length, 1063 const BPoint& pt, escapement_delta* delta) 1064 { 1065 CRASH_IF_NOT_LOCKED 1066 1067 BPoint penLocation = pt; 1068 1069 // try a fast clipping path 1070 if (fPainter->ClippingRegion()) { 1071 float fontSize = fPainter->Font().Size(); 1072 BRect clippingFrame = fPainter->ClippingRegion()->Frame(); 1073 if (pt.x > clippingFrame.right || pt.y + fontSize < clippingFrame.top 1074 || pt.y - fontSize > clippingFrame.bottom) { 1075 penLocation.x += StringWidth(string, length, delta); 1076 return penLocation; 1077 } 1078 } 1079 1080 // use a FontCacheRefernece to speed up the second pass of 1081 // drawing the string 1082 FontCacheReference cacheReference; 1083 1084 //bigtime_t now = system_time(); 1085 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. 1086 // Cursiously, the DrawString after it is actually faster!?! 1087 // TODO: make the availability of the hardware cursor part of the 1088 // HW acceleration flags and skip all calculations for HideFloatingOverlays 1089 // in case we don't have one. 1090 // TODO: Watch out about penLocation and use Painter::PenLocation() when 1091 // not using BoundindBox anymore. 1092 BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta, 1093 &cacheReference); 1094 // stop here if we're supposed to render outside of the clipping 1095 b = fPainter->ClipRect(b); 1096 if (b.IsValid()) { 1097 //printf("bounding box '%s': %lld µs\n", string, system_time() - now); 1098 AutoFloatingOverlaysHider _(fGraphicsCard, b); 1099 1100 //now = system_time(); 1101 BRect touched = fPainter->DrawString(string, length, pt, delta, 1102 &cacheReference); 1103 //printf("drawing string: %lld µs\n", system_time() - now); 1104 1105 _CopyToFront(touched); 1106 } 1107 1108 return penLocation; 1109 } 1110 1111 // StringWidth 1112 float 1113 DrawingEngine::StringWidth(const char* string, int32 length, 1114 escapement_delta* delta) 1115 { 1116 return fPainter->StringWidth(string, length, delta); 1117 } 1118 1119 // StringWidth 1120 float 1121 DrawingEngine::StringWidth(const char* string, int32 length, 1122 const ServerFont& font, escapement_delta* delta) 1123 { 1124 return font.StringWidth(string, length, delta); 1125 } 1126 1127 // #pragma mark - 1128 1129 // DumpToFile 1130 bool 1131 DrawingEngine::DumpToFile(const char *path) 1132 { 1133 CRASH_IF_NOT_EXCLUSIVE_LOCKED 1134 1135 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1136 if (buffer) { 1137 BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1); 1138 SaveToPNG(path, bounds, buffer->ColorSpace(), 1139 buffer->Bits(), 1140 buffer->BitsLength(), 1141 buffer->BytesPerRow()); 1142 return true; 1143 } 1144 return false; 1145 } 1146 1147 // DumpToBitmap 1148 ServerBitmap* 1149 DrawingEngine::DumpToBitmap() 1150 { 1151 return NULL; 1152 } 1153 1154 status_t 1155 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds) 1156 { 1157 CRASH_IF_NOT_EXCLUSIVE_LOCKED 1158 1159 RenderingBuffer *buffer = fGraphicsCard->FrontBuffer(); 1160 if (!buffer) 1161 return B_ERROR; 1162 1163 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1164 bounds = bounds & clip; 1165 AutoFloatingOverlaysHider _(fGraphicsCard, bounds); 1166 1167 status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(), 1168 buffer->BytesPerRow(), buffer->ColorSpace(), 1169 bounds.LeftTop(), BPoint(0, 0), 1170 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1); 1171 1172 if (drawCursor) { 1173 ServerCursorReference cursorRef = fGraphicsCard->Cursor(); 1174 ServerCursor* cursor = cursorRef.Cursor(); 1175 if (!cursor) 1176 return result; 1177 int32 cursorWidth = cursor->Width(); 1178 int32 cursorHeight = cursor->Height(); 1179 1180 BPoint cursorPosition = fGraphicsCard->CursorPosition(); 1181 cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot(); 1182 1183 BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1), 1184 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1185 1186 cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(), 1187 bitmap->BytesPerRow(), bitmap->ColorSpace(), 1188 cursorPosition, BPoint(0, 0), 1189 cursorWidth, cursorHeight); 1190 1191 uint8 *bits = (uint8 *)cursorArea.Bits(); 1192 uint8 *cursorBits = (uint8 *)cursor->Bits(); 1193 for (int32 i = 0; i < cursorHeight; i++) { 1194 for (int32 j = 0; j < cursorWidth; j++) { 1195 uint8 alpha = 255 - cursorBits[3]; 1196 bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0]; 1197 bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1]; 1198 bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2]; 1199 cursorBits += 4; 1200 bits += 4; 1201 } 1202 } 1203 1204 bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(), 1205 cursorArea.BytesPerRow(), cursorArea.ColorSpace(), 1206 BPoint(0, 0), cursorPosition, 1207 cursorWidth, cursorHeight); 1208 } 1209 1210 return result; 1211 } 1212 1213 // #pragma mark - 1214 1215 BRect 1216 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const 1217 { 1218 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1219 BRect dst; 1220 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1221 if (buffer) { 1222 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1223 1224 dst = src; 1225 dst.OffsetBy(xOffset, yOffset); 1226 1227 if (clip.Intersects(src) && clip.Intersects(dst)) { 1228 uint32 bytesPerRow = buffer->BytesPerRow(); 1229 uint8* bits = (uint8*)buffer->Bits(); 1230 1231 // clip source rect 1232 src = src & clip; 1233 // clip dest rect 1234 dst = dst & clip; 1235 // move dest back over source and clip source to dest 1236 dst.OffsetBy(-xOffset, -yOffset); 1237 src = src & dst; 1238 1239 // calc offset in buffer 1240 bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow; 1241 1242 uint32 width = src.IntegerWidth() + 1; 1243 uint32 height = src.IntegerHeight() + 1; 1244 1245 _CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset); 1246 1247 // offset dest again, because it is return value 1248 dst.OffsetBy(xOffset, yOffset); 1249 } 1250 } 1251 return dst; 1252 } 1253 1254 1255 void 1256 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, 1257 uint32 bytesPerRow, int32 xOffset, int32 yOffset) const 1258 { 1259 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1260 int32 xIncrement; 1261 int32 yIncrement; 1262 1263 if (yOffset == 0 && xOffset > 0) { 1264 // copy from right to left 1265 xIncrement = -1; 1266 src += (width - 1) * 4; 1267 } else { 1268 // copy from left to right 1269 xIncrement = 1; 1270 } 1271 1272 if (yOffset > 0) { 1273 // copy from bottom to top 1274 yIncrement = -bytesPerRow; 1275 src += (height - 1) * bytesPerRow; 1276 } else { 1277 // copy from top to bottom 1278 yIncrement = bytesPerRow; 1279 } 1280 1281 uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; 1282 1283 if (xIncrement == 1) { 1284 uint8 tmpBuffer[width * 4]; 1285 for (uint32 y = 0; y < height; y++) { 1286 // NOTE: read into temporary scanline buffer, 1287 // avoid memcpy because it might be graphics card memory 1288 gfxcpy32(tmpBuffer, src, width * 4); 1289 // write back temporary scanline buffer 1290 // NOTE: **don't read and write over the PCI bus 1291 // at the same time** 1292 memcpy(dst, tmpBuffer, width * 4); 1293 // NOTE: this (instead of the two pass copy above) might 1294 // speed up QEMU -> ?!? (would depend on how it emulates 1295 // the PCI bus...) 1296 // TODO: would be nice if we actually knew 1297 // if we're operating in graphics memory or main memory... 1298 //memcpy(dst, src, width * 4); 1299 src += yIncrement; 1300 dst += yIncrement; 1301 } 1302 } else { 1303 for (uint32 y = 0; y < height; y++) { 1304 uint32* srcHandle = (uint32*)src; 1305 uint32* dstHandle = (uint32*)dst; 1306 for (uint32 x = 0; x < width; x++) { 1307 *dstHandle = *srcHandle; 1308 srcHandle += xIncrement; 1309 dstHandle += xIncrement; 1310 } 1311 src += yIncrement; 1312 dst += yIncrement; 1313 } 1314 } 1315 } 1316 1317 1318 inline void 1319 DrawingEngine::_CopyToFront(const BRect& frame) 1320 { 1321 if (fCopyToFront) 1322 fGraphicsCard->Invalidate(frame); 1323 } 1324 1325 1326