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