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