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