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