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 if (!filled) 506 extend_by_stroke_width(clipped, d); 507 clipped = fPainter->ClipRect(clipped); 508 if (clipped.IsValid()) { 509 fGraphicsCard->HideSoftwareCursor(clipped); 510 511 fPainter->SetDrawState(d); 512 fPainter->DrawEllipse(r, filled); 513 514 fGraphicsCard->Invalidate(clipped); 515 fGraphicsCard->ShowSoftwareCursor(); 516 } 517 518 Unlock(); 519 } 520 } 521 522 // DrawPolygon 523 void 524 DrawingEngine::DrawPolygon(BPoint* ptlist, int32 numpts, 525 BRect bounds, const DrawState* d, 526 bool filled, bool closed) 527 { 528 if (Lock()) { 529 make_rect_valid(bounds); 530 if (!filled) 531 extend_by_stroke_width(bounds, d); 532 bounds = fPainter->ClipRect(bounds); 533 if (bounds.IsValid()) { 534 fGraphicsCard->HideSoftwareCursor(bounds); 535 536 fPainter->SetDrawState(d); 537 fPainter->DrawPolygon(ptlist, numpts, filled, closed); 538 539 fGraphicsCard->Invalidate(bounds); 540 fGraphicsCard->ShowSoftwareCursor(); 541 } 542 543 Unlock(); 544 } 545 } 546 547 // #pragma mark - RGBColor 548 549 void 550 DrawingEngine::StrokePoint(const BPoint& pt, const RGBColor &color) 551 { 552 StrokeLine(pt, pt, color); 553 } 554 555 // StrokeLine 556 // 557 // * this function is only used by Decorators 558 // * it assumes a one pixel wide line 559 void 560 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, const RGBColor &color) 561 { 562 if (Lock()) { 563 BRect touched(start, end); 564 make_rect_valid(touched); 565 touched = fPainter->ClipRect(touched); 566 fGraphicsCard->HideSoftwareCursor(touched); 567 568 if (!fPainter->StraightLine(start, end, color.GetColor32())) { 569 DrawState context; 570 context.SetHighColor(color); 571 context.SetDrawingMode(B_OP_OVER); 572 StrokeLine(start, end, &context); 573 } else { 574 fGraphicsCard->Invalidate(touched); 575 } 576 fGraphicsCard->ShowSoftwareCursor(); 577 Unlock(); 578 } 579 } 580 581 // this function is used to draw a one pixel wide rect 582 void 583 DrawingEngine::StrokeRect(BRect r, const RGBColor &color) 584 { 585 if (Lock()) { 586 make_rect_valid(r); 587 BRect clipped = fPainter->ClipRect(r); 588 if (clipped.IsValid()) { 589 fGraphicsCard->HideSoftwareCursor(clipped); 590 591 fPainter->StrokeRect(r, color.GetColor32()); 592 593 fGraphicsCard->Invalidate(clipped); 594 fGraphicsCard->ShowSoftwareCursor(); 595 } 596 597 Unlock(); 598 } 599 } 600 601 602 void 603 DrawingEngine::FillRect(BRect r, const RGBColor& color) 604 { 605 // NOTE: Write locking because we might use HW acceleration. 606 // This needs to be investigated, I'm doing this because of 607 // gut feeling. 608 if (WriteLock()) { 609 make_rect_valid(r); 610 r = fPainter->ClipRect(r); 611 if (r.IsValid()) { 612 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); 613 614 // try hardware optimized version first 615 if (fAvailableHWAccleration & HW_ACC_FILL_REGION) { 616 BRegion region(r); 617 region.IntersectWith(fPainter->ClippingRegion()); 618 fGraphicsCard->FillRegion(region, color, 619 fSuspendSyncLevel == 0 620 || cursorTouched); 621 } else { 622 fPainter->FillRect(r, color.GetColor32()); 623 624 fGraphicsCard->Invalidate(r); 625 } 626 627 if (cursorTouched) 628 fGraphicsCard->ShowSoftwareCursor(); 629 } 630 631 WriteUnlock(); 632 } 633 } 634 635 636 void 637 DrawingEngine::FillRegion(BRegion& r, const RGBColor& color) 638 { 639 // NOTE: Write locking because we might use HW acceleration. 640 // This needs to be investigated, I'm doing this because of 641 // gut feeling. 642 // NOTE: region expected to be already clipped correctly!! 643 if (WriteLock()) { 644 BRect frame = r.Frame(); 645 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(frame); 646 647 bool doInSoftware = true; 648 // try hardware optimized version first 649 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0 650 && frame.Width() * frame.Height() > 100) { 651 fGraphicsCard->FillRegion(r, color, fSuspendSyncLevel == 0 652 || cursorTouched); 653 doInSoftware = false; 654 } 655 656 if (doInSoftware) { 657 658 int32 count = r.CountRects(); 659 for (int32 i = 0; i < count; i++) { 660 fPainter->FillRectNoClipping(r.RectAt(i), color.GetColor32()); 661 } 662 663 fGraphicsCard->Invalidate(r.Frame()); 664 } 665 666 if (cursorTouched) 667 fGraphicsCard->ShowSoftwareCursor(); 668 669 WriteUnlock(); 670 } 671 } 672 673 // #pragma mark - DrawState 674 675 void 676 DrawingEngine::StrokeRect(BRect r, const DrawState *d) 677 { 678 // support invalid rects 679 make_rect_valid(r); 680 BRect clipped(r); 681 extend_by_stroke_width(clipped, d); 682 clipped = fPainter->ClipRect(clipped); 683 if (clipped.IsValid()) { 684 685 fGraphicsCard->HideSoftwareCursor(clipped); 686 687 fPainter->SetDrawState(d); 688 fPainter->StrokeRect(r); 689 690 fGraphicsCard->Invalidate(clipped); 691 fGraphicsCard->ShowSoftwareCursor(); 692 } 693 } 694 695 696 void 697 DrawingEngine::FillRect(BRect r, const DrawState *d) 698 { 699 // NOTE: Write locking because we might use HW acceleration. 700 // This needs to be investigated, I'm doing this because of 701 // gut feeling. 702 if (WriteLock()) { 703 make_rect_valid(r); 704 r = fPainter->ClipRect(r); 705 if (r.IsValid()) { 706 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(r); 707 708 bool doInSoftware = true; 709 if ((r.Width() + 1) * (r.Height() + 1) > 100.0) { 710 // try hardware optimized version first 711 // if the rect is large enough 712 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 713 if (d->GetPattern() == B_SOLID_HIGH 714 && (d->GetDrawingMode() == B_OP_COPY 715 || d->GetDrawingMode() == B_OP_OVER)) { 716 BRegion region(r); 717 region.IntersectWith(fPainter->ClippingRegion()); 718 fGraphicsCard->FillRegion(region, d->HighColor(), 719 fSuspendSyncLevel == 0 720 || cursorTouched); 721 doInSoftware = false; 722 } else if (d->GetPattern() == B_SOLID_LOW 723 && d->GetDrawingMode() == B_OP_COPY) { 724 BRegion region(r); 725 region.IntersectWith(fPainter->ClippingRegion()); 726 fGraphicsCard->FillRegion(region, d->LowColor(), 727 fSuspendSyncLevel == 0 728 || cursorTouched); 729 doInSoftware = false; 730 } 731 } 732 } 733 if (doInSoftware) { 734 fPainter->SetDrawState(d); 735 fPainter->FillRect(r); 736 737 fGraphicsCard->Invalidate(r); 738 } 739 740 if (cursorTouched) 741 fGraphicsCard->ShowSoftwareCursor(); 742 } 743 744 WriteUnlock(); 745 } 746 } 747 748 749 void 750 DrawingEngine::FillRegion(BRegion& r, const DrawState *d) 751 { 752 // NOTE: Write locking because we might use HW acceleration. 753 // This needs to be investigated, I'm doing this because of 754 // gut feeling. 755 if (WriteLock()) { 756 BRect clipped = fPainter->ClipRect(r.Frame()); 757 if (clipped.IsValid()) { 758 bool cursorTouched = fGraphicsCard->HideSoftwareCursor(clipped); 759 760 bool doInSoftware = true; 761 // try hardware optimized version first 762 if ((fAvailableHWAccleration & HW_ACC_FILL_REGION) != 0) { 763 if (d->GetPattern() == B_SOLID_HIGH 764 && (d->GetDrawingMode() == B_OP_COPY 765 || d->GetDrawingMode() == B_OP_OVER)) { 766 r.IntersectWith(fPainter->ClippingRegion()); 767 fGraphicsCard->FillRegion(r, d->HighColor(), 768 fSuspendSyncLevel == 0 769 || cursorTouched); 770 doInSoftware = false; 771 } else if (d->GetPattern() == B_SOLID_LOW 772 && d->GetDrawingMode() == B_OP_COPY) { 773 r.IntersectWith(fPainter->ClippingRegion()); 774 fGraphicsCard->FillRegion(r, d->LowColor(), 775 fSuspendSyncLevel == 0 776 || cursorTouched); 777 doInSoftware = false; 778 } 779 } 780 781 if (doInSoftware) { 782 fPainter->SetDrawState(d); 783 784 BRect touched = fPainter->FillRect(r.RectAt(0)); 785 786 int32 count = r.CountRects(); 787 for (int32 i = 1; i < count; i++) { 788 touched = touched | fPainter->FillRect(r.RectAt(i)); 789 } 790 791 fGraphicsCard->Invalidate(touched); 792 } 793 794 if (cursorTouched) 795 fGraphicsCard->ShowSoftwareCursor(); 796 } 797 798 WriteUnlock(); 799 } 800 } 801 802 803 void 804 DrawingEngine::DrawRoundRect(BRect r, float xrad, float yrad, 805 const DrawState* d, bool filled) 806 { 807 // NOTE: the stroke does not extend past "r" in R5, 808 // though I consider this unexpected behaviour. 809 make_rect_valid(r); 810 BRect clipped = fPainter->ClipRect(r); 811 if (clipped.IsValid()) { 812 fGraphicsCard->HideSoftwareCursor(clipped); 813 814 fPainter->SetDrawState(d); 815 BRect touched = filled ? fPainter->FillRoundRect(r, xrad, yrad) 816 : fPainter->StrokeRoundRect(r, xrad, yrad); 817 818 fGraphicsCard->Invalidate(touched); 819 fGraphicsCard->ShowSoftwareCursor(); 820 } 821 } 822 823 824 void 825 DrawingEngine::DrawShape(const BRect& bounds, int32 opCount, 826 const uint32* opList, int32 ptCount, const BPoint* ptList, 827 const DrawState* d, bool filled) 828 { 829 fGraphicsCard->HideSoftwareCursor(); 830 831 fPainter->SetDrawState(d); 832 BRect touched = fPainter->DrawShape(opCount, opList, 833 ptCount, ptList, 834 filled); 835 836 fGraphicsCard->Invalidate(touched); 837 fGraphicsCard->ShowSoftwareCursor(); 838 } 839 840 841 void 842 DrawingEngine::DrawTriangle(BPoint* pts, const BRect& bounds, 843 const DrawState* d, bool filled) 844 { 845 BRect clipped(bounds); 846 if (!filled) 847 extend_by_stroke_width(clipped, d); 848 clipped = fPainter->ClipRect(clipped); 849 if (clipped.IsValid()) { 850 fGraphicsCard->HideSoftwareCursor(clipped); 851 852 fPainter->SetDrawState(d); 853 if (filled) 854 fPainter->FillTriangle(pts[0], pts[1], pts[2]); 855 else 856 fPainter->StrokeTriangle(pts[0], pts[1], pts[2]); 857 858 fGraphicsCard->Invalidate(clipped); 859 fGraphicsCard->ShowSoftwareCursor(); 860 } 861 } 862 863 // StrokeLine 864 void 865 DrawingEngine::StrokeLine(const BPoint &start, const BPoint &end, DrawState* context) 866 { 867 BRect touched(start, end); 868 make_rect_valid(touched); 869 extend_by_stroke_width(touched, context); 870 touched = fPainter->ClipRect(touched); 871 if (touched.IsValid()) { 872 fGraphicsCard->HideSoftwareCursor(touched); 873 874 fPainter->SetDrawState(context); 875 touched = fPainter->StrokeLine(start, end); 876 877 fGraphicsCard->Invalidate(touched); 878 fGraphicsCard->ShowSoftwareCursor(); 879 } 880 } 881 882 // StrokeLineArray 883 void 884 DrawingEngine::StrokeLineArray(int32 numLines, 885 const LineArrayData *linedata, const DrawState *d) 886 { 887 if (!d || !linedata || numLines <= 0) 888 return; 889 890 // figure out bounding box for line array 891 const LineArrayData *data = (const LineArrayData *)&(linedata[0]); 892 BRect touched(min_c(data->pt1.x, data->pt2.x), 893 min_c(data->pt1.y, data->pt2.y), 894 max_c(data->pt1.x, data->pt2.x), 895 max_c(data->pt1.y, data->pt2.y)); 896 897 for (int32 i = 1; i < numLines; i++) { 898 data = (const LineArrayData *)&(linedata[i]); 899 BRect box(min_c(data->pt1.x, data->pt2.x), 900 min_c(data->pt1.y, data->pt2.y), 901 max_c(data->pt1.x, data->pt2.x), 902 max_c(data->pt1.y, data->pt2.y)); 903 touched = touched | box; 904 } 905 extend_by_stroke_width(touched, d); 906 touched = fPainter->ClipRect(touched); 907 if (touched.IsValid()) { 908 fGraphicsCard->HideSoftwareCursor(touched); 909 910 data = (const LineArrayData *)&(linedata[0]); 911 912 DrawState context; 913 context.SetDrawingMode(d->GetDrawingMode()); 914 context.SetLowColor(d->LowColor()); 915 context.SetHighColor(data->color); 916 context.SetPenSize(d->PenSize()); 917 // pen size is already correctly scaled 918 919 fPainter->SetDrawState(&context); 920 921 fPainter->StrokeLine(data->pt1, data->pt2); 922 923 for (int32 i = 1; i < numLines; i++) { 924 data = (const LineArrayData *)&(linedata[i]); 925 fPainter->SetHighColor(data->color); 926 fPainter->StrokeLine(data->pt1, data->pt2); 927 } 928 929 fGraphicsCard->Invalidate(touched); 930 fGraphicsCard->ShowSoftwareCursor(); 931 } 932 } 933 934 // #pragma mark - 935 936 BPoint 937 DrawingEngine::DrawString(const char* string, int32 length, 938 const BPoint& pt, DrawState* d, 939 escapement_delta* delta) 940 { 941 // TODO: use delta 942 FontLocker locker(d); 943 944 BPoint penLocation = pt; 945 946 fPainter->SetDrawState(d, true); 947 //bigtime_t now = system_time(); 948 // TODO: BoundingBox is quite slow!! Optimizing it will be beneficial. 949 // Cursiously, the DrawString after it is actually faster!?! 950 // TODO: make the availability of the hardware cursor part of the 951 // HW acceleration flags and skip all calculations for HideSoftwareCursor 952 // in case we don't have one. 953 // TODO: Watch out about penLocation and use Painter::PenLocation() when 954 // not using BoundindBox anymore. 955 BRect b = fPainter->BoundingBox(string, length, pt, &penLocation, delta); 956 // stop here if we're supposed to render outside of the clipping 957 b = fPainter->ClipRect(b); 958 if (b.IsValid()) { 959 //printf("bounding box '%s': %lld µs\n", string, system_time() - now); 960 fGraphicsCard->HideSoftwareCursor(b); 961 962 //now = system_time(); 963 BRect touched = fPainter->DrawString(string, length, pt, delta); 964 //printf("drawing string: %lld µs\n", system_time() - now); 965 966 fGraphicsCard->Invalidate(touched); 967 fGraphicsCard->ShowSoftwareCursor(); 968 } 969 970 return penLocation; 971 } 972 973 // StringWidth 974 float 975 DrawingEngine::StringWidth(const char* string, int32 length, 976 const DrawState* d, escapement_delta* delta) 977 { 978 // TODO: use delta 979 FontLocker locker(d); 980 981 float width = 0.0; 982 // if (Lock()) { 983 // NOTE: For now it is enough to block on the 984 // font style lock, this already prevents multiple 985 // threads from executing this code and avoids a 986 // deadlock in case another thread holds the font 987 // lock already and then tries to lock the drawing 988 // engine after it is already locked here (race condition) 989 width = fPainter->StringWidth(string, length, d); 990 // Unlock(); 991 // } 992 return width; 993 } 994 995 // StringWidth 996 float 997 DrawingEngine::StringWidth(const char* string, int32 length, 998 const ServerFont& font, escapement_delta* delta) 999 { 1000 DrawState d; 1001 d.SetFont(font); 1002 return StringWidth(string, length, &d, delta); 1003 } 1004 1005 // StringHeight 1006 float 1007 DrawingEngine::StringHeight(const char *string, int32 length, 1008 const DrawState *d) 1009 { 1010 FontLocker locker(d); 1011 1012 float height = 0.0; 1013 // if (Lock()) { 1014 // NOTE: For now it is enough to block on the 1015 // font style lock, this already prevents multiple 1016 // threads from executing this code and avoids a 1017 // deadlock in case another thread holds the font 1018 // lock already and then tries to lock the drawing 1019 // engine after it is already locked here (race condition) 1020 fPainter->SetDrawState(d, true); 1021 BPoint dummy1(0.0, 0.0); 1022 BPoint dummy2(0.0, 0.0); 1023 height = fPainter->BoundingBox(string, length, dummy1, &dummy2).Height(); 1024 // Unlock(); 1025 // } 1026 return height; 1027 } 1028 1029 // #pragma mark - 1030 1031 // Lock 1032 bool 1033 DrawingEngine::Lock() 1034 { 1035 return fGraphicsCard->WriteLock(); 1036 } 1037 1038 // Unlock 1039 void 1040 DrawingEngine::Unlock() 1041 { 1042 fGraphicsCard->WriteUnlock(); 1043 } 1044 1045 // WriteLock 1046 bool 1047 DrawingEngine::WriteLock() 1048 { 1049 return fGraphicsCard->WriteLock(); 1050 } 1051 1052 // WriteUnlock 1053 void 1054 DrawingEngine::WriteUnlock() 1055 { 1056 fGraphicsCard->WriteUnlock(); 1057 } 1058 1059 // #pragma mark - 1060 1061 // DumpToFile 1062 bool 1063 DrawingEngine::DumpToFile(const char *path) 1064 { 1065 if (Lock()) { 1066 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1067 if (buffer) { 1068 BRect bounds(0.0, 0.0, buffer->Width() - 1, buffer->Height() - 1); 1069 SaveToPNG(path, bounds, buffer->ColorSpace(), 1070 buffer->Bits(), 1071 buffer->BitsLength(), 1072 buffer->BytesPerRow()); 1073 } 1074 Unlock(); 1075 } 1076 return true; 1077 } 1078 1079 // DumpToBitmap 1080 ServerBitmap* 1081 DrawingEngine::DumpToBitmap() 1082 { 1083 return NULL; 1084 } 1085 1086 status_t 1087 DrawingEngine::ReadBitmap(ServerBitmap *bitmap, bool drawCursor, BRect bounds) 1088 { 1089 if (Lock()) { 1090 RenderingBuffer *buffer = fGraphicsCard->DrawingBuffer(); 1091 if (!buffer) 1092 return B_ERROR; 1093 1094 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1095 bounds = bounds & clip; 1096 fGraphicsCard->HideSoftwareCursor(bounds); 1097 1098 status_t result = bitmap->ImportBits(buffer->Bits(), buffer->BitsLength(), 1099 buffer->BytesPerRow(), buffer->ColorSpace(), 1100 bounds.LeftTop(), BPoint(0, 0), 1101 bounds.IntegerWidth() + 1, bounds.IntegerHeight() + 1); 1102 1103 if (drawCursor) { 1104 ServerCursor *cursor = fGraphicsCard->Cursor(); 1105 int32 cursorWidth = cursor->Width(); 1106 int32 cursorHeight = cursor->Height(); 1107 1108 BPoint cursorPosition = fGraphicsCard->GetCursorPosition(); 1109 cursorPosition -= bounds.LeftTop() + cursor->GetHotSpot(); 1110 1111 BBitmap cursorArea(BRect(0, 0, cursorWidth - 1, cursorHeight - 1), 1112 B_BITMAP_NO_SERVER_LINK, B_RGBA32); 1113 1114 cursorArea.ImportBits(bitmap->Bits(), bitmap->BitsLength(), 1115 bitmap->BytesPerRow(), bitmap->ColorSpace(), 1116 cursorPosition, BPoint(0, 0), 1117 cursorWidth, cursorHeight); 1118 1119 uint8 *bits = (uint8 *)cursorArea.Bits(); 1120 uint8 *cursorBits = (uint8 *)cursor->Bits(); 1121 for (int32 i = 0; i < cursorHeight; i++) { 1122 for (int32 j = 0; j < cursorWidth; j++) { 1123 uint8 alpha = 255 - cursorBits[3]; 1124 bits[0] = ((bits[0] * alpha) >> 8) + cursorBits[0]; 1125 bits[1] = ((bits[1] * alpha) >> 8) + cursorBits[1]; 1126 bits[2] = ((bits[2] * alpha) >> 8) + cursorBits[2]; 1127 cursorBits += 4; 1128 bits += 4; 1129 } 1130 } 1131 1132 bitmap->ImportBits(cursorArea.Bits(), cursorArea.BitsLength(), 1133 cursorArea.BytesPerRow(), cursorArea.ColorSpace(), 1134 BPoint(0, 0), cursorPosition, 1135 cursorWidth, cursorHeight); 1136 } 1137 1138 fGraphicsCard->ShowSoftwareCursor(); 1139 Unlock(); 1140 return result; 1141 } 1142 1143 return B_ERROR; 1144 } 1145 1146 // #pragma mark - 1147 1148 BRect 1149 DrawingEngine::_CopyRect(BRect src, int32 xOffset, int32 yOffset) const 1150 { 1151 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1152 BRect dst; 1153 RenderingBuffer* buffer = fGraphicsCard->DrawingBuffer(); 1154 if (buffer) { 1155 BRect clip(0, 0, buffer->Width() - 1, buffer->Height() - 1); 1156 1157 dst = src; 1158 dst.OffsetBy(xOffset, yOffset); 1159 1160 if (clip.Intersects(src) && clip.Intersects(dst)) { 1161 uint32 bytesPerRow = buffer->BytesPerRow(); 1162 uint8* bits = (uint8*)buffer->Bits(); 1163 1164 // clip source rect 1165 src = src & clip; 1166 // clip dest rect 1167 dst = dst & clip; 1168 // move dest back over source and clip source to dest 1169 dst.OffsetBy(-xOffset, -yOffset); 1170 src = src & dst; 1171 1172 // calc offset in buffer 1173 bits += (int32)src.left * 4 + (int32)src.top * bytesPerRow; 1174 1175 uint32 width = src.IntegerWidth() + 1; 1176 uint32 height = src.IntegerHeight() + 1; 1177 1178 _CopyRect(bits, width, height, bytesPerRow, xOffset, yOffset); 1179 1180 // offset dest again, because it is return value 1181 dst.OffsetBy(xOffset, yOffset); 1182 } 1183 } 1184 return dst; 1185 } 1186 1187 1188 void 1189 DrawingEngine::_CopyRect(uint8* src, uint32 width, uint32 height, 1190 uint32 bytesPerRow, int32 xOffset, int32 yOffset) const 1191 { 1192 // TODO: assumes drawing buffer is 32 bits (which it currently always is) 1193 int32 xIncrement; 1194 int32 yIncrement; 1195 1196 if (yOffset == 0 && xOffset > 0) { 1197 // copy from right to left 1198 xIncrement = -1; 1199 src += (width - 1) * 4; 1200 } else { 1201 // copy from left to right 1202 xIncrement = 1; 1203 } 1204 1205 if (yOffset > 0) { 1206 // copy from bottom to top 1207 yIncrement = -bytesPerRow; 1208 src += (height - 1) * bytesPerRow; 1209 } else { 1210 // copy from top to bottom 1211 yIncrement = bytesPerRow; 1212 } 1213 1214 uint8* dst = src + yOffset * bytesPerRow + xOffset * 4; 1215 1216 if (xIncrement == 1) { 1217 uint8 tmpBuffer[width * 4]; 1218 for (uint32 y = 0; y < height; y++) { 1219 // NOTE: read into temporary scanline buffer, 1220 // avoid memcpy because it might be graphics card memory 1221 gfxcpy32(tmpBuffer, src, width * 4); 1222 // write back temporary scanline buffer 1223 // NOTE: **don't read and write over the PCI bus 1224 // at the same time** 1225 memcpy(dst, tmpBuffer, width * 4); 1226 // NOTE: this (instead of the two pass copy above) might 1227 // speed up QEMU -> ?!? (would depend on how it emulates 1228 // the PCI bus...) 1229 // TODO: would be nice if we actually knew 1230 // if we're operating in graphics memory or main memory... 1231 //memcpy(dst, src, width * 4); 1232 src += yIncrement; 1233 dst += yIncrement; 1234 } 1235 } else { 1236 for (uint32 y = 0; y < height; y++) { 1237 uint32* srcHandle = (uint32*)src; 1238 uint32* dstHandle = (uint32*)dst; 1239 for (uint32 x = 0; x < width; x++) { 1240 *dstHandle = *srcHandle; 1241 srcHandle += xIncrement; 1242 dstHandle += xIncrement; 1243 } 1244 src += yIncrement; 1245 dst += yIncrement; 1246 } 1247 } 1248 } 1249 1250 1251