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 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame); 745 746 // try hardware optimized version first 747 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 748 && frame.Width() * frame.Height() > 100) { 749 fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 750 || cursorTouched); 751 } else { 752 int32 count = r.CountRects(); 753 for (int32 i = 0; i < count; i++) { 754 fPainter->FillRectNoClipping(r.RectAt(i), color); 755 } 756 757 fGraphicsCard->Invalidate(frame); 758 } 759 760 if (cursorTouched) 761 fGraphicsCard->ShowSoftwareCursor(); 762 } 763 764 // #pragma mark - DrawState 765 766 void 767 DrawingEngine::StrokeRect(BRect r) 768 { 769 CRASH_IF_NOT_LOCKED 770 771 // support invalid rects 772 make_rect_valid(r); 773 BRect clipped(r); 774 extend_by_stroke_width(clipped, fPainter->PenSize()); 775 clipped = fPainter->ClipRect(clipped); 776 if (clipped.IsValid()) { 777 778 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); 779 780 fPainter->StrokeRect(r); 781 782 fGraphicsCard->Invalidate(clipped); 783 if (cursorTouched) 784 fGraphicsCard->ShowSoftwareCursor(); 785 } 786 } 787 788 789 void 790 DrawingEngine::FillRect(BRect r) 791 { 792 CRASH_IF_NOT_LOCKED 793 794 make_rect_valid(r); 795 r = fPainter->AlignAndClipRect(r); 796 if (r.IsValid()) { 797 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); 798 799 bool doInSoftware = true; 800 if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { 801 // try hardware optimized version first 802 // if the rect is large enough 803 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 804 if (fPainter->Pattern() == B_SOLID_HIGH 805 && (fPainter->DrawingMode() == B_OP_COPY 806 || fPainter->DrawingMode() == B_OP_OVER)) { 807 BRegion region(r); 808 region.IntersectWith(fPainter->ClippingRegion()); 809 fGraphicsCard->FillRegion(region, fPainter->HighColor(), 810 fSuspendSyncLevel == 0 811 || cursorTouched); 812 doInSoftware = false; 813 } else if (fPainter->Pattern() == B_SOLID_LOW 814 && fPainter->DrawingMode() == B_OP_COPY) { 815 BRegion region(r); 816 region.IntersectWith(fPainter->ClippingRegion()); 817 fGraphicsCard->FillRegion(region, fPainter->LowColor(), 818 fSuspendSyncLevel == 0 819 || cursorTouched); 820 doInSoftware = false; 821 } 822 } 823 } 824 825 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 826 && fPainter->Pattern() == B_SOLID_HIGH 827 && fPainter->DrawingMode() == B_OP_INVERT) { 828 BRegion region(r); 829 region.IntersectWith(fPainter->ClippingRegion()); 830 fGraphicsCard->InvertRegion(region); 831 doInSoftware = false; 832 } 833 834 if (doInSoftware) { 835 fPainter->FillRect(r); 836 837 fGraphicsCard->Invalidate(r); 838 } 839 840 if (cursorTouched) 841 fGraphicsCard->ShowSoftwareCursor(); 842 } 843 } 844 845 846 void 847 DrawingEngine::FillRegion(BRegion& r) 848 { 849 CRASH_IF_NOT_LOCKED 850 851 BRect clipped = fPainter->ClipRect(r.Frame()); 852 if (clipped.IsValid()) { 853 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); 854 855 bool doInSoftware = true; 856 // try hardware optimized version first 857 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 858 if (fPainter->Pattern() == B_SOLID_HIGH 859 && (fPainter->DrawingMode() == B_OP_COPY 860 || fPainter->DrawingMode() == B_OP_OVER)) { 861 r.IntersectWith(fPainter->ClippingRegion()); 862 fGraphicsCard->FillRegion(r, fPainter->HighColor(), 863 fSuspendSyncLevel == 0 864 || cursorTouched); 865 doInSoftware = false; 866 } else if (fPainter->Pattern() == B_SOLID_LOW 867 && fPainter->DrawingMode() == B_OP_COPY) { 868 r.IntersectWith(fPainter->ClippingRegion()); 869 fGraphicsCard->FillRegion(r, fPainter->LowColor(), 870 fSuspendSyncLevel == 0 871 || cursorTouched); 872 doInSoftware = false; 873 } 874 } 875 876 if (doInSoftware && fAvailableHWAccleration & HW_ACC_INVERT_REGION 877 && fPainter->Pattern() == B_SOLID_HIGH 878 && fPainter->DrawingMode() == B_OP_INVERT) { 879 r.IntersectWith(fPainter->ClippingRegion()); 880 fGraphicsCard->InvertRegion(r); 881 doInSoftware = false; 882 } 883 884 if (doInSoftware) { 885 886 BRect touched = fPainter->FillRect(r.RectAt(0)); 887 888 int32 count = r.CountRects(); 889 for (int32 i = 1; i < count; i++) { 890 touched = touched | fPainter->FillRect(r.RectAt(i)); 891 } 892 893 fGraphicsCard->Invalidate(touched); 894 } 895 896 if (cursorTouched) 897 fGraphicsCard->ShowSoftwareCursor(); 898 } 899 } 900 901 902 void 903 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, bool filled) 904 { 905 CRASH_IF_NOT_LOCKED 906 907 // NOTE: the stroke does not extend past "r" in R5, 908 // though I consider this unexpected behaviour. 909 make_rect_valid(r); 910 BRect clipped = fPainter->ClipRect(r); 911 912 clipped.left = floorf(clipped.left); 913 clipped.top = floorf(clipped.top); 914 clipped.right = ceilf(clipped.right); 915 clipped.bottom = ceilf(clipped.bottom); 916 917 if (clipped.IsValid()) { 918 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); 919 920 BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad) 921 : fPainter->StrokeRoundRect(r, xrad, yrad); 922 923 fGraphicsCard->Invalidate(touched); 924 if (cursorTouched) 925 fGraphicsCard->ShowSoftwareCursor(); 926 } 927 } 928 929 930 void 931 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 932 const uint32* opList, int32 ptCount, const BPoint* ptList, bool filled) 933 { 934 CRASH_IF_NOT_LOCKED 935 936 // NOTE: hides cursor regardless of if and where 937 // shape is drawn on screen, TODO: optimize 938 fGraphicsCard->HideSoftwareCursor(); 939 940 BRect touched = fPainter->DrawShape(opCount, opList, 941 ptCount, ptList, 942 filled); 943 944 fGraphicsCard->Invalidate(touched); 945 fGraphicsCard->ShowSoftwareCursor(); 946 } 947 948 949 void 950 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, bool filled) 951 { 952 CRASH_IF_NOT_LOCKED 953 954 BRect clipped(bounds); 955 if (!filled) 956 extend_by_stroke_width(clipped, fPainter->PenSize()); 957 clipped = fPainter->ClipRect(clipped); 958 if (clipped.IsValid()) { 959 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); 960 961 if (filled) 962 fPainter->FillTriangle(pts[0], pts[1], pts[2]); 963 else 964 fPainter->StrokeTriangle(pts[0], pts[1], pts[2]); 965 966 fGraphicsCard->Invalidate(clipped); 967 if (cursorTouched) 968 fGraphicsCard->ShowSoftwareCursor(); 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 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(touched); 984 985 fPainter->StrokeLine(start, end); 986 987 fGraphicsCard->Invalidate(touched); 988 if (cursorTouched) 989 fGraphicsCard->ShowSoftwareCursor(); 990 } 991 } 992 993 // StrokeLineArray 994 void 995 DrawingEngine::StrokeLineArray(int32 numLines, 996 const LineArrayData *linedata) 997 { 998 CRASH_IF_NOT_LOCKED 999 1000 if (!linedata || numLines <= 0) 1001 return; 1002 1003 // figure out bounding box for line array 1004 const LineArrayData *data = (const LineArrayData *)&(linedata[0]); 1005 BRect touched(min_c(data->pt1.x, data->pt2.x), 1006 min_c(data->pt1.y, data->pt2.y), 1007 max_c(data->pt1.x, data->pt2.x), 1008 max_c(data->pt1.y, data->pt2.y)); 1009 1010 for (int32 i = 1; i < numLines; i++) { 1011 data = (const LineArrayData *)&(linedata[i]); 1012 BRect box(min_c(data->pt1.x, data->pt2.x), 1013 min_c(data->pt1.y, data->pt2.y), 1014 max_c(data->pt1.x, data->pt2.x), 1015 max_c(data->pt1.y, data->pt2.y)); 1016 touched = touched | box; 1017 } 1018 extend_by_stroke_width(touched, fPainter->PenSize()); 1019 touched = fPainter->ClipRect(touched); 1020 if (touched.IsValid()) { 1021 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(touched); 1022 1023 data = (const LineArrayData *)&(linedata[0]); 1024 1025 // store current graphics state, we mess with the 1026 // high color and pattern... 1027 rgb_color oldColor = fPainter->HighColor(); 1028 struct pattern pattern = fPainter->Pattern(); 1029 1030 fPainter->SetHighColor(data->color); 1031 fPainter->SetPattern(B_SOLID_HIGH); 1032 fPainter->StrokeLine(data->pt1, data->pt2); 1033 1034 for (int32 i = 1; i < numLines; i++) { 1035 data = (const LineArrayData *)&(linedata[i]); 1036 fPainter->SetHighColor(data->color); 1037 fPainter->StrokeLine(data->pt1, data->pt2); 1038 } 1039 1040 // restore correct drawing state highcolor and pattern 1041 fPainter->SetHighColor(oldColor); 1042 fPainter->SetPattern(pattern); 1043 1044 fGraphicsCard->Invalidate(touched); 1045 if (cursorTouched) 1046 fGraphicsCard->ShowSoftwareCursor(); 1047 } 1048 } 1049 1050 // #pragma mark - 1051 1052 BPoint 1053 DrawingEngine::DrawString(const char* string, int32 length, 1054 const BPoint& pt, escapement_delta* delta) 1055 { 1056 CRASH_IF_NOT_LOCKED 1057 1058 BPoint penLocation = pt; 1059 1060 // try a fast clipping path 1061 float fontSize = fPainter->Font().Size(); 1062 BRect clippingFrame = fPainter->ClippingRegion()->Frame(); 1063 if (pt.x > clippingFrame.right || pt.y + fontSize < clippingFrame.top 1064 || pt.y - fontSize > clippingFrame.bottom) { 1065 penLocation.x += StringWidth(string, length, delta); 1066 return penLocation; 1067 } 1068 1069 // use a FontCacheRefernece to speed up the second pass of 1070 // drawing the string 1071 FontCacheReference cacheReference; 1072 1073 //bigtime_t now = system_time(); 1074 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. 1075 // Cursiously, the DrawString after it is actually faster!?! 1076 // TODO: make the availability of the hardware cursor part of the 1077 // HW acceleration flags and skip all calculations for HideSoftwareCursor 1078 // in case we don't have one. 1079 // TODO: Watch out about penLocation and use Painter::PenLocation() when 1080 // not using BoundindBox anymore. 1081 BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta, 1082 &cacheReference); 1083 // stop here if we're supposed to render outside of the clipping 1084 b = fPainter->ClipRect(b); 1085 if (b.IsValid()) { 1086 //printf("bounding box '%s': %lld µs\n", string, system_time() - now); 1087 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(b); 1088 1089 //now = system_time(); 1090 BRect touched = fPainter->DrawString(string, length, pt, delta, 1091 &cacheReference); 1092 //printf("drawing string: %lld µs\n", system_time() - now); 1093 1094 fGraphicsCard->Invalidate(touched); 1095 if (cursorTouched) 1096 fGraphicsCard->ShowSoftwareCursor(); 1097 } 1098 1099 return penLocation; 1100 } 1101 1102 // StringWidth 1103 float 1104 DrawingEngine::StringWidth(const char* string, int32 length, 1105 escapement_delta* delta) 1106 { 1107 return fPainter->StringWidth(string, length, delta); 1108 } 1109 1110 // StringWidth 1111 float 1112 DrawingEngine::StringWidth(const char* string, int32 length, 1113 const ServerFont& font, escapement_delta* delta) 1114 { 1115 return font.StringWidth(string, length, delta); 1116 } 1117 1118 // #pragma mark - 1119 1120 // DumpToFile 1121 bool 1122 DrawingEngine::DumpToFile(const char *path) 1123 { 1124 CRASH_IF_NOT_EXCLUSIVE_LOCKED 1125 1126 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1127 if (buffer) { 1128 BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1); 1129 SaveToPNG(path, bounds, buffer->ColorSpace(), 1130 buffer->Bits(), 1131 buffer->BitsLength(), 1132 buffer->BytesPerRow()); 1133 return true; 1134 } 1135 return false; 1136 } 1137 1138 // DumpToBitmap 1139 ServerBitmap* 1140 DrawingEngine::DumpToBitmap() 1141 { 1142 return NULL; 1143 } 1144 1145 status_t 1146 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds) 1147 { 1148 CRASH_IF_NOT_EXCLUSIVE_LOCKED 1149 1150 RenderingBuffer *buffer = fGraphicsCard->DrawingBuffer(); 1151 if (!buffer) 1152 return B_ERROR; 1153 1154 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1155 bounds = bounds & clip; 1156 fGraphicsCard->HideSoftwareCursor(bounds); 1157 1158 status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(), 1159 buffer->BytesPerRow(), buffer->ColorSpace(), 1160 bounds.LeftTop(), BPoint(0, 0), 1161 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1); 1162 1163 if (drawCursor) { 1164 ServerCursor *cursor = fGraphicsCard->Cursor(); 1165 int32 cursorWidth = cursor->Width(); 1166 int32 cursorHeight = cursor->Height(); 1167 1168 BPoint cursorPosition = fGraphicsCard->CursorPosition(); 1169 cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot(); 1170 1171 BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1), 1172 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1173 1174 cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(), 1175 bitmap->BytesPerRow(), bitmap->ColorSpace(), 1176 cursorPosition, BPoint(0, 0), 1177 cursorWidth, cursorHeight); 1178 1179 uint8 *bits = (uint8 *)cursorArea.Bits(); 1180 uint8 *cursorBits = (uint8 *)cursor->Bits(); 1181 for (int32 i = 0; i < cursorHeight; i++) { 1182 for (int32 j = 0; j < cursorWidth; j++) { 1183 uint8 alpha = 255 - cursorBits[3]; 1184 bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0]; 1185 bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1]; 1186 bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2]; 1187 cursorBits += 4; 1188 bits += 4; 1189 } 1190 } 1191 1192 bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(), 1193 cursorArea.BytesPerRow(), cursorArea.ColorSpace(), 1194 BPoint(0, 0), cursorPosition, 1195 cursorWidth, cursorHeight); 1196 } 1197 1198 fGraphicsCard->ShowSoftwareCursor(); 1199 1200 return result; 1201 } 1202 1203 // #pragma mark - 1204 1205 BRect 1206 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const 1207 { 1208 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1209 BRect dst; 1210 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1211 if (buffer) { 1212 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1213 1214 dst = src; 1215 dst.OffsetBy(xOffset, yOffset); 1216 1217 if (clip.Intersects(src) && clip.Intersects(dst)) { 1218 uint32 bytesPerRow = buffer->BytesPerRow(); 1219 uint8* bits = (uint8*)buffer->Bits(); 1220 1221 // clip source rect 1222 src = src & clip; 1223 // clip dest rect 1224 dst = dst & clip; 1225 // move dest back over source and clip source to dest 1226 dst.OffsetBy(-xOffset, -yOffset); 1227 src = src & dst; 1228 1229 // calc offset in buffer 1230 bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow; 1231 1232 uint32 width = src.IntegerWidth() + 1; 1233 uint32 height = src.IntegerHeight() + 1; 1234 1235 _CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset); 1236 1237 // offset dest again, because it is return value 1238 dst.OffsetBy(xOffset, yOffset); 1239 } 1240 } 1241 return dst; 1242 } 1243 1244 1245 void 1246 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, 1247 uint32 bytesPerRow, int32 xOffset, int32 yOffset) const 1248 { 1249 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1250 int32 xIncrement; 1251 int32 yIncrement; 1252 1253 if (yOffset == 0 && xOffset > 0) { 1254 // copy from right to left 1255 xIncrement = -1; 1256 src += (width - 1) * 4; 1257 } else { 1258 // copy from left to right 1259 xIncrement = 1; 1260 } 1261 1262 if (yOffset > 0) { 1263 // copy from bottom to top 1264 yIncrement = -bytesPerRow; 1265 src += (height - 1) * bytesPerRow; 1266 } else { 1267 // copy from top to bottom 1268 yIncrement = bytesPerRow; 1269 } 1270 1271 uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; 1272 1273 if (xIncrement == 1) { 1274 uint8 tmpBuffer[width * 4]; 1275 for (uint32 y = 0; y < height; y++) { 1276 // NOTE: read into temporary scanline buffer, 1277 // avoid memcpy because it might be graphics card memory 1278 gfxcpy32(tmpBuffer, src, width * 4); 1279 // write back temporary scanline buffer 1280 // NOTE: **don't read and write over the PCI bus 1281 // at the same time** 1282 memcpy(dst, tmpBuffer, width * 4); 1283 // NOTE: this (instead of the two pass copy above) might 1284 // speed up QEMU -> ?!? (would depend on how it emulates 1285 // the PCI bus...) 1286 // TODO: would be nice if we actually knew 1287 // if we're operating in graphics memory or main memory... 1288 //memcpy(dst, src, width * 4); 1289 src += yIncrement; 1290 dst += yIncrement; 1291 } 1292 } else { 1293 for (uint32 y = 0; y < height; y++) { 1294 uint32* srcHandle = (uint32*)src; 1295 uint32* dstHandle = (uint32*)dst; 1296 for (uint32 x = 0; x < width; x++) { 1297 *dstHandle = *srcHandle; 1298 srcHandle += xIncrement; 1299 dstHandle += xIncrement; 1300 } 1301 src += yIncrement; 1302 dst += yIncrement; 1303 } 1304 } 1305 } 1306 1307 1308