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