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