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