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