1 /* 2 * Copyright 2005-2012, Haiku. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 10 #include "HWInterface.h" 11 12 #include <new> 13 #include <stdio.h> 14 #include <string.h> 15 #include <unistd.h> 16 17 #include <vesa/vesa_info.h> 18 19 #include "drawing_support.h" 20 21 #include "DrawingEngine.h" 22 #include "RenderingBuffer.h" 23 #include "SystemPalette.h" 24 #include "UpdateQueue.h" 25 26 27 using std::nothrow; 28 29 30 HWInterfaceListener::HWInterfaceListener() 31 { 32 } 33 34 35 HWInterfaceListener::~HWInterfaceListener() 36 { 37 } 38 39 40 // #pragma mark - HWInterface 41 42 43 HWInterface::HWInterface(bool doubleBuffered, bool enableUpdateQueue) 44 : 45 MultiLocker("hw interface lock"), 46 fFloatingOverlaysLock("floating overlays lock"), 47 fCursor(NULL), 48 fDragBitmap(NULL), 49 fDragBitmapOffset(0, 0), 50 fCursorAndDragBitmap(NULL), 51 fCursorVisible(false), 52 fCursorObscured(false), 53 fHardwareCursorEnabled(false), 54 fCursorLocation(0, 0), 55 fDoubleBuffered(doubleBuffered), 56 fVGADevice(-1), 57 fUpdateExecutor(NULL), 58 fListeners(20) 59 { 60 SetAsyncDoubleBuffered(doubleBuffered && enableUpdateQueue); 61 } 62 63 64 HWInterface::~HWInterface() 65 { 66 SetAsyncDoubleBuffered(false); 67 } 68 69 70 status_t 71 HWInterface::Initialize() 72 { 73 return MultiLocker::InitCheck(); 74 } 75 76 77 DrawingEngine* 78 HWInterface::CreateDrawingEngine() 79 { 80 return new(std::nothrow) DrawingEngine(this); 81 } 82 83 84 EventStream* 85 HWInterface::CreateEventStream() 86 { 87 return NULL; 88 } 89 90 91 status_t 92 HWInterface::GetAccelerantPath(BString &path) 93 { 94 return B_ERROR; 95 } 96 97 98 status_t 99 HWInterface::GetDriverPath(BString &path) 100 { 101 return B_ERROR; 102 } 103 104 105 status_t 106 HWInterface::GetPreferredMode(display_mode* mode) 107 { 108 return B_NOT_SUPPORTED; 109 } 110 111 112 status_t 113 HWInterface::GetMonitorInfo(monitor_info* info) 114 { 115 return B_NOT_SUPPORTED; 116 } 117 118 119 // #pragma mark - 120 121 122 void 123 HWInterface::SetCursor(ServerCursor* cursor) 124 { 125 if (!fFloatingOverlaysLock.Lock()) 126 return; 127 128 if (fCursor.Get() != cursor) { 129 BRect oldFrame = _CursorFrame(); 130 131 fCursor.SetTo(cursor); 132 133 Invalidate(oldFrame); 134 135 _AdoptDragBitmap(); 136 Invalidate(_CursorFrame()); 137 } 138 fFloatingOverlaysLock.Unlock(); 139 } 140 141 142 ServerCursorReference 143 HWInterface::Cursor() const 144 { 145 if (!fFloatingOverlaysLock.Lock()) 146 return ServerCursorReference(NULL); 147 148 fFloatingOverlaysLock.Unlock(); 149 return fCursor; 150 } 151 152 153 ServerCursorReference 154 HWInterface::CursorAndDragBitmap() const 155 { 156 if (!fFloatingOverlaysLock.Lock()) 157 return ServerCursorReference(NULL); 158 159 fFloatingOverlaysLock.Unlock(); 160 return fCursorAndDragBitmap; 161 } 162 163 164 void 165 HWInterface::SetCursorVisible(bool visible) 166 { 167 if (!fFloatingOverlaysLock.Lock()) 168 return; 169 170 if (fCursorVisible != visible) { 171 // NOTE: _CursorFrame() will 172 // return an invalid rect if 173 // fCursorVisible == false! 174 if (visible) { 175 fCursorVisible = visible; 176 fCursorObscured = false; 177 IntRect r = _CursorFrame(); 178 179 _DrawCursor(r); 180 Invalidate(r); 181 } else { 182 IntRect r = _CursorFrame(); 183 fCursorVisible = visible; 184 185 _RestoreCursorArea(); 186 Invalidate(r); 187 } 188 } 189 fFloatingOverlaysLock.Unlock(); 190 } 191 192 193 bool 194 HWInterface::IsCursorVisible() 195 { 196 bool visible = true; 197 if (fFloatingOverlaysLock.Lock()) { 198 visible = fCursorVisible; 199 fFloatingOverlaysLock.Unlock(); 200 } 201 return visible; 202 } 203 204 205 void 206 HWInterface::ObscureCursor() 207 { 208 if (!fFloatingOverlaysLock.Lock()) 209 return; 210 211 if (!fCursorObscured) { 212 SetCursorVisible(false); 213 fCursorObscured = true; 214 } 215 fFloatingOverlaysLock.Unlock(); 216 } 217 218 219 void 220 HWInterface::MoveCursorTo(float x, float y) 221 { 222 if (!fFloatingOverlaysLock.Lock()) 223 return; 224 225 BPoint p(x, y); 226 if (p != fCursorLocation) { 227 // unhide cursor if it is obscured only 228 if (fCursorObscured) { 229 SetCursorVisible(true); 230 } 231 IntRect oldFrame = _CursorFrame(); 232 fCursorLocation = p; 233 if (fCursorVisible) { 234 // Invalidate and _DrawCursor would not draw 235 // anything if the cursor is hidden 236 // (invalid cursor frame), but explicitly 237 // testing for it here saves us some cycles 238 if (fCursorAreaBackup.Get() != NULL) { 239 // means we have a software cursor which we need to draw 240 _RestoreCursorArea(); 241 _DrawCursor(_CursorFrame()); 242 } 243 IntRect newFrame = _CursorFrame(); 244 if (newFrame.Intersects(oldFrame)) 245 Invalidate(oldFrame | newFrame); 246 else { 247 Invalidate(oldFrame); 248 Invalidate(newFrame); 249 } 250 } 251 } 252 fFloatingOverlaysLock.Unlock(); 253 } 254 255 256 BPoint 257 HWInterface::CursorPosition() 258 { 259 BPoint location; 260 if (fFloatingOverlaysLock.Lock()) { 261 location = fCursorLocation; 262 fFloatingOverlaysLock.Unlock(); 263 } 264 return location; 265 } 266 267 268 void 269 HWInterface::SetDragBitmap(const ServerBitmap* bitmap, 270 const BPoint& offsetFromCursor) 271 { 272 if (fFloatingOverlaysLock.Lock()) { 273 fDragBitmap.SetTo((ServerBitmap*)bitmap, false); 274 fDragBitmapOffset = offsetFromCursor; 275 _AdoptDragBitmap(); 276 fFloatingOverlaysLock.Unlock(); 277 } 278 } 279 280 281 // #pragma mark - 282 283 284 RenderingBuffer* 285 HWInterface::DrawingBuffer() const 286 { 287 if (IsDoubleBuffered()) 288 return BackBuffer(); 289 return FrontBuffer(); 290 } 291 292 293 void 294 HWInterface::SetAsyncDoubleBuffered(bool doubleBuffered) 295 { 296 if (doubleBuffered) { 297 if (fUpdateExecutor.Get() != NULL) 298 return; 299 fUpdateExecutor.SetTo(new (nothrow) UpdateQueue(this)); 300 AddListener(fUpdateExecutor.Get()); 301 } else { 302 if (fUpdateExecutor.Get() == NULL) 303 return; 304 RemoveListener(fUpdateExecutor.Get()); 305 fUpdateExecutor.Unset(); 306 } 307 } 308 309 310 bool 311 HWInterface::IsDoubleBuffered() const 312 { 313 return fDoubleBuffered; 314 } 315 316 317 /*! The object needs to be already locked! 318 */ 319 status_t 320 HWInterface::InvalidateRegion(const BRegion& region) 321 { 322 int32 count = region.CountRects(); 323 for (int32 i = 0; i < count; i++) { 324 status_t result = Invalidate(region.RectAt(i)); 325 if (result != B_OK) 326 return result; 327 } 328 329 return B_OK; 330 } 331 332 333 /*! The object needs to be already locked! 334 */ 335 status_t 336 HWInterface::Invalidate(const BRect& frame) 337 { 338 if (IsDoubleBuffered()) { 339 #if 0 340 // NOTE: The UpdateQueue works perfectly fine, but it screws the 341 // flicker-free-ness of the double buffered rendering. The problem being the 342 // asynchronous nature. The UpdateQueue will transfer regions of the screen 343 // which have been clean at the time we are in this function, but which have 344 // been damaged meanwhile by drawing into them again. All in all, the 345 // UpdateQueue is good for reducing the number of times that the transfer 346 // is performed, and makes it happen during refresh only, but until there 347 // is a smarter way to synchronize this all better, I've disabled it. 348 if (fUpdateExecutor != NULL) { 349 fUpdateExecutor->AddRect(frame); 350 return B_OK; 351 } 352 #endif 353 return CopyBackToFront(frame); 354 } 355 return B_OK; 356 } 357 358 359 /*! The object must already be locked! 360 */ 361 status_t 362 HWInterface::CopyBackToFront(const BRect& frame) 363 { 364 RenderingBuffer* frontBuffer = FrontBuffer(); 365 RenderingBuffer* backBuffer = BackBuffer(); 366 367 if (!backBuffer || !frontBuffer) 368 return B_NO_INIT; 369 370 // we need to mess with the area, but it is const 371 IntRect area(frame); 372 IntRect bufferClip(backBuffer->Bounds()); 373 374 if (area.IsValid() && area.Intersects(bufferClip)) { 375 376 // make sure we don't copy out of bounds 377 area = bufferClip & area; 378 379 bool cursorLocked = fFloatingOverlaysLock.Lock(); 380 381 BRegion region((BRect)area); 382 if (IsDoubleBuffered()) 383 region.Exclude((clipping_rect)_CursorFrame()); 384 385 _CopyBackToFront(region); 386 387 _DrawCursor(area); 388 389 if (cursorLocked) 390 fFloatingOverlaysLock.Unlock(); 391 392 return B_OK; 393 } 394 return B_BAD_VALUE; 395 } 396 397 398 void 399 HWInterface::_CopyBackToFront(/*const*/ BRegion& region) 400 { 401 RenderingBuffer* backBuffer = BackBuffer(); 402 403 uint32 srcBPR = backBuffer->BytesPerRow(); 404 uint8* src = (uint8*)backBuffer->Bits(); 405 406 int32 count = region.CountRects(); 407 for (int32 i = 0; i < count; i++) { 408 clipping_rect r = region.RectAtInt(i); 409 // offset to left top pixel in source buffer (always B_RGBA32) 410 uint8* srcOffset = src + r.top * srcBPR + r.left * 4; 411 _CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom); 412 } 413 } 414 415 416 // #pragma mark - 417 418 419 overlay_token 420 HWInterface::AcquireOverlayChannel() 421 { 422 return NULL; 423 } 424 425 426 void 427 HWInterface::ReleaseOverlayChannel(overlay_token token) 428 { 429 } 430 431 432 status_t 433 HWInterface::GetOverlayRestrictions(const Overlay* overlay, 434 overlay_restrictions* restrictions) 435 { 436 return B_NOT_SUPPORTED; 437 } 438 439 440 bool 441 HWInterface::CheckOverlayRestrictions(int32 width, int32 height, 442 color_space colorSpace) 443 { 444 return false; 445 } 446 447 448 const overlay_buffer* 449 HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space) 450 { 451 return NULL; 452 } 453 454 455 void 456 HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer) 457 { 458 } 459 460 461 void 462 HWInterface::ConfigureOverlay(Overlay* overlay) 463 { 464 } 465 466 467 void 468 HWInterface::HideOverlay(Overlay* overlay) 469 { 470 } 471 472 473 // #pragma mark - 474 475 476 bool 477 HWInterface::HideFloatingOverlays(const BRect& area) 478 { 479 if (IsDoubleBuffered()) 480 return false; 481 if (!fFloatingOverlaysLock.Lock()) 482 return false; 483 if (fCursorAreaBackup.Get() != NULL && !fCursorAreaBackup->cursor_hidden) { 484 BRect backupArea(fCursorAreaBackup->left, fCursorAreaBackup->top, 485 fCursorAreaBackup->right, fCursorAreaBackup->bottom); 486 if (area.Intersects(backupArea)) { 487 _RestoreCursorArea(); 488 // do not unlock the cursor lock 489 return true; 490 } 491 } 492 fFloatingOverlaysLock.Unlock(); 493 return false; 494 } 495 496 497 bool 498 HWInterface::HideFloatingOverlays() 499 { 500 if (IsDoubleBuffered()) 501 return false; 502 if (!fFloatingOverlaysLock.Lock()) 503 return false; 504 505 _RestoreCursorArea(); 506 return true; 507 } 508 509 510 void 511 HWInterface::ShowFloatingOverlays() 512 { 513 if (fCursorAreaBackup.Get() != NULL && fCursorAreaBackup->cursor_hidden) 514 _DrawCursor(_CursorFrame()); 515 516 fFloatingOverlaysLock.Unlock(); 517 } 518 519 520 // #pragma mark - 521 522 523 bool 524 HWInterface::AddListener(HWInterfaceListener* listener) 525 { 526 if (listener && !fListeners.HasItem(listener)) 527 return fListeners.AddItem(listener); 528 return false; 529 } 530 531 532 void 533 HWInterface::RemoveListener(HWInterfaceListener* listener) 534 { 535 fListeners.RemoveItem(listener); 536 } 537 538 539 // #pragma mark - 540 541 542 /*! Default implementation, can be used as fallback or for software cursor. 543 \param area is where we potentially draw the cursor, the cursor 544 might be somewhere else, in which case this function does nothing 545 */ 546 void 547 HWInterface::_DrawCursor(IntRect area) const 548 { 549 RenderingBuffer* backBuffer = DrawingBuffer(); 550 if (!backBuffer || !area.IsValid()) 551 return; 552 553 IntRect cf = _CursorFrame(); 554 555 // make sure we don't copy out of bounds 556 area = backBuffer->Bounds() & area; 557 558 if (cf.IsValid() && area.Intersects(cf)) { 559 560 // clip to common area 561 area = area & cf; 562 563 int32 width = area.right - area.left + 1; 564 int32 height = area.bottom - area.top + 1; 565 566 // make a bitmap from the backbuffer 567 // that has the cursor blended on top of it 568 569 // blending buffer 570 uint8* buffer = new(std::nothrow) uint8[width * height * 4]; 571 // TODO: cache this buffer 572 if (buffer == NULL) 573 return; 574 575 // offset into back buffer 576 uint8* src = (uint8*)backBuffer->Bits(); 577 uint32 srcBPR = backBuffer->BytesPerRow(); 578 src += area.top * srcBPR + area.left * 4; 579 580 // offset into cursor bitmap 581 uint8* crs = (uint8*)fCursorAndDragBitmap->Bits(); 582 uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow(); 583 // since area is clipped to cf, 584 // the diff between area.top and cf.top is always positive, 585 // same for diff between area.left and cf.left 586 crs += (area.top - (int32)floorf(cf.top)) * crsBPR 587 + (area.left - (int32)floorf(cf.left)) * 4; 588 589 uint8* dst = buffer; 590 591 if (fCursorAreaBackup.Get() != NULL && fCursorAreaBackup->buffer 592 && fFloatingOverlaysLock.Lock()) { 593 fCursorAreaBackup->cursor_hidden = false; 594 // remember which area the backup contains 595 fCursorAreaBackup->left = area.left; 596 fCursorAreaBackup->top = area.top; 597 fCursorAreaBackup->right = area.right; 598 fCursorAreaBackup->bottom = area.bottom; 599 uint8* bup = fCursorAreaBackup->buffer; 600 uint32 bupBPR = fCursorAreaBackup->bpr; 601 602 // blending and backup of drawing buffer 603 for (int32 y = area.top; y <= area.bottom; y++) { 604 uint8* s = src; 605 uint8* c = crs; 606 uint8* d = dst; 607 uint8* b = bup; 608 609 for (int32 x = area.left; x <= area.right; x++) { 610 *(uint32*)b = *(uint32*)s; 611 // assumes backbuffer alpha = 255 612 // assuming pre-multiplied cursor bitmap 613 int a = 255 - c[3]; 614 d[0] = ((int)(b[0] * a + 255) >> 8) + c[0]; 615 d[1] = ((int)(b[1] * a + 255) >> 8) + c[1]; 616 d[2] = ((int)(b[2] * a + 255) >> 8) + c[2]; 617 618 s += 4; 619 c += 4; 620 d += 4; 621 b += 4; 622 } 623 crs += crsBPR; 624 src += srcBPR; 625 dst += width * 4; 626 bup += bupBPR; 627 } 628 fFloatingOverlaysLock.Unlock(); 629 } else { 630 // blending 631 for (int32 y = area.top; y <= area.bottom; y++) { 632 uint8* s = src; 633 uint8* c = crs; 634 uint8* d = dst; 635 for (int32 x = area.left; x <= area.right; x++) { 636 // assumes backbuffer alpha = 255 637 // assuming pre-multiplied cursor bitmap 638 uint8 a = 255 - c[3]; 639 d[0] = ((s[0] * a + 255) >> 8) + c[0]; 640 d[1] = ((s[1] * a + 255) >> 8) + c[1]; 641 d[2] = ((s[2] * a + 255) >> 8) + c[2]; 642 643 s += 4; 644 c += 4; 645 d += 4; 646 } 647 crs += crsBPR; 648 src += srcBPR; 649 dst += width * 4; 650 } 651 } 652 // copy result to front buffer 653 _CopyToFront(buffer, width * 4, area.left, area.top, area.right, 654 area.bottom); 655 656 delete[] buffer; 657 } 658 } 659 660 661 /*! - source is assumed to be already at the right offset 662 - source is assumed to be in B_RGBA32 format 663 - location in front buffer is calculated 664 - conversion from B_RGBA32 to format of front buffer is taken care of 665 */ 666 void 667 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y, 668 int32 right, int32 bottom) const 669 { 670 RenderingBuffer* frontBuffer = FrontBuffer(); 671 672 uint8* dst = (uint8*)frontBuffer->Bits(); 673 uint32 dstBPR = frontBuffer->BytesPerRow(); 674 675 // transfer, handle colorspace conversion 676 switch (frontBuffer->ColorSpace()) { 677 case B_RGB32: 678 case B_RGBA32: 679 { 680 int32 bytes = (right - x + 1) * 4; 681 682 if (bytes > 0) { 683 // offset to left top pixel in dest buffer 684 dst += y * dstBPR + x * 4; 685 // copy 686 for (; y <= bottom; y++) { 687 // bytes is guaranteed to be multiple of 4 688 gfxcpy32(dst, src, bytes); 689 dst += dstBPR; 690 src += srcBPR; 691 } 692 } 693 break; 694 } 695 696 case B_RGB24: 697 { 698 // offset to left top pixel in dest buffer 699 dst += y * dstBPR + x * 3; 700 int32 left = x; 701 // copy 702 for (; y <= bottom; y++) { 703 uint8* srcHandle = src; 704 uint8* dstHandle = dst; 705 for (x = left; x <= right; x++) { 706 dstHandle[0] = srcHandle[0]; 707 dstHandle[1] = srcHandle[1]; 708 dstHandle[2] = srcHandle[2]; 709 dstHandle += 3; 710 srcHandle += 4; 711 } 712 dst += dstBPR; 713 src += srcBPR; 714 } 715 break; 716 } 717 718 case B_RGB16: 719 { 720 // offset to left top pixel in dest buffer 721 dst += y * dstBPR + x * 2; 722 int32 left = x; 723 // copy 724 // TODO: assumes BGR order, does this work on big endian as well? 725 for (; y <= bottom; y++) { 726 uint8* srcHandle = src; 727 uint16* dstHandle = (uint16*)dst; 728 for (x = left; x <= right; x++) { 729 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8) 730 | ((srcHandle[1] & 0xfc) << 3) | (srcHandle[0] >> 3)); 731 dstHandle ++; 732 srcHandle += 4; 733 } 734 dst += dstBPR; 735 src += srcBPR; 736 } 737 break; 738 } 739 740 case B_RGB15: 741 case B_RGBA15: 742 { 743 // offset to left top pixel in dest buffer 744 dst += y * dstBPR + x * 2; 745 int32 left = x; 746 // copy 747 // TODO: assumes BGR order, does this work on big endian as well? 748 for (; y <= bottom; y++) { 749 uint8* srcHandle = src; 750 uint16* dstHandle = (uint16*)dst; 751 for (x = left; x <= right; x++) { 752 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7) 753 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3)); 754 dstHandle ++; 755 srcHandle += 4; 756 } 757 dst += dstBPR; 758 src += srcBPR; 759 } 760 break; 761 } 762 763 case B_CMAP8: 764 { 765 const color_map *colorMap = SystemColorMap(); 766 // offset to left top pixel in dest buffer 767 dst += y * dstBPR + x; 768 int32 left = x; 769 uint16 index; 770 // copy 771 // TODO: assumes BGR order again 772 for (; y <= bottom; y++) { 773 uint8* srcHandle = src; 774 uint8* dstHandle = dst; 775 for (x = left; x <= right; x++) { 776 index = ((srcHandle[2] & 0xf8) << 7) 777 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3); 778 *dstHandle = colorMap->index_map[index]; 779 dstHandle ++; 780 srcHandle += 4; 781 } 782 dst += dstBPR; 783 src += srcBPR; 784 } 785 786 break; 787 } 788 789 case B_GRAY8: 790 if (frontBuffer->Width() > dstBPR) { 791 // VGA 16 color grayscale planar mode 792 if (fVGADevice >= 0) { 793 vga_planar_blit_args args; 794 args.source = src; 795 args.source_bytes_per_row = srcBPR; 796 args.left = x; 797 args.top = y; 798 args.right = right; 799 args.bottom = bottom; 800 if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args)) 801 == 0) 802 break; 803 } 804 805 // Since we cannot set the plane, we do monochrome output 806 dst += y * dstBPR + x / 8; 807 int32 left = x; 808 809 // TODO: this is awfully slow... 810 // TODO: assumes BGR order 811 for (; y <= bottom; y++) { 812 uint8* srcHandle = src; 813 uint8* dstHandle = dst; 814 uint8 current8 = dstHandle[0]; 815 // we store 8 pixels before writing them back 816 817 for (x = left; x <= right; x++) { 818 uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1] 819 + 116 * srcHandle[0]) / 1024; 820 srcHandle += 4; 821 822 if (pixel > 128) 823 current8 |= 0x80 >> (x & 7); 824 else 825 current8 &= ~(0x80 >> (x & 7)); 826 827 if ((x & 7) == 7) { 828 // last pixel in 8 pixel group 829 dstHandle[0] = current8; 830 dstHandle++; 831 current8 = dstHandle[0]; 832 } 833 } 834 835 if (x & 7) { 836 // last pixel has not been written yet 837 dstHandle[0] = current8; 838 } 839 dst += dstBPR; 840 src += srcBPR; 841 } 842 } else { 843 // offset to left top pixel in dest buffer 844 dst += y * dstBPR + x; 845 int32 left = x; 846 // copy 847 // TODO: assumes BGR order, does this work on big endian as well? 848 for (; y <= bottom; y++) { 849 uint8* srcHandle = src; 850 uint8* dstHandle = dst; 851 for (x = left; x <= right; x++) { 852 *dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1] 853 + 116 * srcHandle[0]) / 1024; 854 dstHandle ++; 855 srcHandle += 4; 856 } 857 dst += dstBPR; 858 src += srcBPR; 859 } 860 } 861 break; 862 863 default: 864 fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported " 865 "front buffer format! (0x%x)\n", frontBuffer->ColorSpace()); 866 break; 867 } 868 } 869 870 871 /*! The object must be locked 872 */ 873 IntRect 874 HWInterface::_CursorFrame() const 875 { 876 IntRect frame(0, 0, -1, -1); 877 if (fCursorAndDragBitmap && fCursorVisible && !fHardwareCursorEnabled) { 878 frame = fCursorAndDragBitmap->Bounds(); 879 frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot()); 880 } 881 return frame; 882 } 883 884 885 void 886 HWInterface::_RestoreCursorArea() const 887 { 888 if (fCursorAreaBackup.Get() != NULL && !fCursorAreaBackup->cursor_hidden) { 889 _CopyToFront(fCursorAreaBackup->buffer, fCursorAreaBackup->bpr, 890 fCursorAreaBackup->left, fCursorAreaBackup->top, 891 fCursorAreaBackup->right, fCursorAreaBackup->bottom); 892 893 fCursorAreaBackup->cursor_hidden = true; 894 } 895 } 896 897 898 void 899 HWInterface::_AdoptDragBitmap() 900 { 901 // TODO: support other colorspaces/convert bitmap 902 if (fDragBitmap && !(fDragBitmap->ColorSpace() == B_RGB32 903 || fDragBitmap->ColorSpace() == B_RGBA32)) { 904 fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet " 905 "unsupported colorspace\n"); 906 return; 907 } 908 909 _RestoreCursorArea(); 910 BRect oldCursorFrame = _CursorFrame(); 911 912 if (fDragBitmap != NULL && fDragBitmap->Bounds().Width() > 0 && fDragBitmap->Bounds().Height() > 0) { 913 BRect bitmapFrame = fDragBitmap->Bounds(); 914 if (fCursor) { 915 // put bitmap frame and cursor frame into the same 916 // coordinate space (the cursor location is the origin) 917 bitmapFrame.OffsetTo(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y)); 918 919 BRect cursorFrame(fCursor->Bounds()); 920 BPoint hotspot(fCursor->GetHotSpot()); 921 // the hotspot is at the origin 922 cursorFrame.OffsetTo(-hotspot.x, -hotspot.y); 923 924 BRect combindedBounds = bitmapFrame | cursorFrame; 925 926 BPoint shift; 927 shift.x = -combindedBounds.left; 928 shift.y = -combindedBounds.top; 929 930 combindedBounds.OffsetBy(shift); 931 cursorFrame.OffsetBy(shift); 932 bitmapFrame.OffsetBy(shift); 933 934 fCursorAndDragBitmap.SetTo(new(std::nothrow) ServerCursor(combindedBounds, 935 fDragBitmap->ColorSpace(), 0, shift), true); 936 937 uint8* dst = fCursorAndDragBitmap ? (uint8*)fCursorAndDragBitmap->Bits() : NULL; 938 if (dst == NULL) { 939 // Oops, we could not allocate memory for the drag bitmap. 940 // Let's show the cursor only. 941 fCursorAndDragBitmap = fCursor; 942 } else { 943 // clear the combined buffer 944 uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow(); 945 946 memset(dst, 0, fCursorAndDragBitmap->BitsLength()); 947 948 // put drag bitmap into combined buffer 949 uint8* src = (uint8*)fDragBitmap->Bits(); 950 uint32 srcBPR = fDragBitmap->BytesPerRow(); 951 952 dst += (int32)bitmapFrame.top * dstBPR 953 + (int32)bitmapFrame.left * 4; 954 955 uint32 width = bitmapFrame.IntegerWidth() + 1; 956 uint32 height = bitmapFrame.IntegerHeight() + 1; 957 958 for (uint32 y = 0; y < height; y++) { 959 memcpy(dst, src, srcBPR); 960 dst += dstBPR; 961 src += srcBPR; 962 } 963 964 // compose cursor into combined buffer 965 dst = (uint8*)fCursorAndDragBitmap->Bits(); 966 dst += (int32)cursorFrame.top * dstBPR 967 + (int32)cursorFrame.left * 4; 968 969 src = (uint8*)fCursor->Bits(); 970 srcBPR = fCursor->BytesPerRow(); 971 972 width = cursorFrame.IntegerWidth() + 1; 973 height = cursorFrame.IntegerHeight() + 1; 974 975 for (uint32 y = 0; y < height; y++) { 976 uint8* d = dst; 977 uint8* s = src; 978 for (uint32 x = 0; x < width; x++) { 979 // takes two semi-transparent pixels 980 // with unassociated alpha (not pre-multiplied) 981 // and stays within non-premultiplied color space 982 if (s[3] > 0) { 983 if (s[3] == 255) { 984 d[0] = s[0]; 985 d[1] = s[1]; 986 d[2] = s[2]; 987 d[3] = 255; 988 } else { 989 uint8 alphaRest = 255 - s[3]; 990 uint32 alphaTemp 991 = (65025 - alphaRest * (255 - d[3])); 992 uint32 alphaDest = d[3] * alphaRest; 993 uint32 alphaSrc = 255 * s[3]; 994 d[0] = (d[0] * alphaDest + s[0] * alphaSrc) 995 / alphaTemp; 996 d[1] = (d[1] * alphaDest + s[1] * alphaSrc) 997 / alphaTemp; 998 d[2] = (d[2] * alphaDest + s[2] * alphaSrc) 999 / alphaTemp; 1000 d[3] = alphaTemp / 255; 1001 } 1002 } 1003 // TODO: make sure the alpha is always upside down, 1004 // then it doesn't need to be done when drawing the cursor 1005 // (see _DrawCursor()) 1006 // d[3] = 255 - d[3]; 1007 d += 4; 1008 s += 4; 1009 } 1010 dst += dstBPR; 1011 src += srcBPR; 1012 } 1013 1014 // handle pre-multiplication with alpha 1015 // for faster compositing during cursor drawing 1016 width = combindedBounds.IntegerWidth() + 1; 1017 height = combindedBounds.IntegerHeight() + 1; 1018 1019 dst = (uint8*)fCursorAndDragBitmap->Bits(); 1020 1021 for (uint32 y = 0; y < height; y++) { 1022 uint8* d = dst; 1023 for (uint32 x = 0; x < width; x++) { 1024 d[0] = (d[0] * d[3]) >> 8; 1025 d[1] = (d[1] * d[3]) >> 8; 1026 d[2] = (d[2] * d[3]) >> 8; 1027 d += 4; 1028 } 1029 dst += dstBPR; 1030 } 1031 } 1032 } else { 1033 fCursorAndDragBitmap.SetTo(new ServerCursor(fDragBitmap->Bits(), 1034 bitmapFrame.IntegerWidth() + 1, bitmapFrame.IntegerHeight() + 1, 1035 fDragBitmap->ColorSpace()), true); 1036 fCursorAndDragBitmap->SetHotSpot(BPoint(-fDragBitmapOffset.x, -fDragBitmapOffset.y)); 1037 } 1038 } else { 1039 fCursorAndDragBitmap = fCursor; 1040 } 1041 1042 Invalidate(oldCursorFrame); 1043 1044 fCursorAreaBackup.Unset(); 1045 1046 if (!fCursorAndDragBitmap) 1047 return; 1048 1049 if (fCursorAndDragBitmap && !IsDoubleBuffered()) { 1050 BRect cursorBounds = fCursorAndDragBitmap->Bounds(); 1051 fCursorAreaBackup.SetTo(new buffer_clip(cursorBounds.IntegerWidth() + 1, 1052 cursorBounds.IntegerHeight() + 1)); 1053 if (fCursorAreaBackup->buffer == NULL) 1054 fCursorAreaBackup.Unset(); 1055 } 1056 _DrawCursor(_CursorFrame()); 1057 } 1058 1059 1060 void 1061 HWInterface::_NotifyFrameBufferChanged() 1062 { 1063 BList listeners(fListeners); 1064 int32 count = listeners.CountItems(); 1065 for (int32 i = 0; i < count; i++) { 1066 HWInterfaceListener* listener 1067 = (HWInterfaceListener*)listeners.ItemAtFast(i); 1068 listener->FrameBufferChanged(); 1069 } 1070 } 1071 1072 1073 void 1074 HWInterface::_NotifyScreenChanged() 1075 { 1076 BList listeners(fListeners); 1077 int32 count = listeners.CountItems(); 1078 for (int32 i = 0; i < count; i++) { 1079 HWInterfaceListener* listener 1080 = (HWInterfaceListener*)listeners.ItemAtFast(i); 1081 listener->ScreenChanged(this); 1082 } 1083 } 1084 1085 1086 /*static*/ bool 1087 HWInterface::_IsValidMode(const display_mode& mode) 1088 { 1089 // TODO: more of those! 1090 if (mode.virtual_width < 320 1091 || mode.virtual_height < 200) 1092 return false; 1093 1094 return true; 1095 } 1096 1097