1 // HWInterface.cpp 2 3 #include <stdio.h> 4 #include <string.h> 5 6 #include "RenderingBuffer.h" 7 #include "ServerCursor.h" 8 #include "SystemPalette.h" 9 #include "UpdateQueue.h" 10 11 #include "HWInterface.h" 12 13 // constructor 14 HWInterface::HWInterface(bool doubleBuffered) 15 : MultiLocker("hw interface lock"), 16 fCursorAreaBackup(NULL), 17 fCursor(NULL), 18 fCursorVisible(false), 19 fCursorLocation(0, 0), 20 fDoubleBuffered(doubleBuffered), 21 // fUpdateExecutor(new UpdateQueue(this)) 22 fUpdateExecutor(NULL) 23 { 24 } 25 26 // destructor 27 HWInterface::~HWInterface() 28 { 29 delete fCursorAreaBackup; 30 delete fCursor; 31 delete fUpdateExecutor; 32 } 33 34 // Initialize 35 status_t 36 HWInterface::Initialize() 37 { 38 return MultiLocker::InitCheck(); 39 } 40 41 // SetCursor 42 void 43 HWInterface::SetCursor(ServerCursor* cursor) 44 { 45 if (WriteLock()) { 46 if (fCursor != cursor) { 47 BRect oldFrame = _CursorFrame(); 48 delete fCursor; 49 delete fCursorAreaBackup; 50 fCursor = cursor; 51 Invalidate(oldFrame); 52 BRect r = _CursorFrame(); 53 if (fCursor && !IsDoubleBuffered()) { 54 BRect cursorBounds = fCursor->Bounds(); 55 fCursorAreaBackup = new buffer_clip(cursorBounds.IntegerWidth() + 1, 56 cursorBounds.IntegerHeight() + 1); 57 _DrawCursor(r); 58 } else 59 fCursorAreaBackup = NULL; 60 Invalidate(r); 61 } 62 WriteUnlock(); 63 } 64 } 65 66 // SetCursorVisible 67 void 68 HWInterface::SetCursorVisible(bool visible) 69 { 70 if (WriteLock()) { 71 if (fCursorVisible != visible) { 72 fCursorVisible = visible; 73 Invalidate(_CursorFrame()); 74 } 75 WriteUnlock(); 76 } 77 } 78 79 // IsCursorVisible 80 bool 81 HWInterface::IsCursorVisible() 82 { 83 bool visible = true; 84 if (ReadLock()) { 85 visible = fCursorVisible; 86 ReadUnlock(); 87 } 88 return visible; 89 } 90 91 // MoveCursorTo 92 void 93 HWInterface::MoveCursorTo(const float& x, const float& y) 94 { 95 if (WriteLock()) { 96 BPoint p(x, y); 97 if (p != fCursorLocation) { 98 BRect oldFrame = _CursorFrame(); 99 fCursorLocation = p; 100 if (fCursorVisible) { 101 // Invalidate and _DrawCursor would not draw 102 // anything if the cursor is hidden 103 // (invalid cursor frame), but explicitly 104 // testing for it here saves us some cycles 105 if (fCursorAreaBackup) { 106 // means we have a software cursor which we need to draw 107 _RestoreCursorArea(); 108 _DrawCursor(_CursorFrame()); 109 } 110 Invalidate(oldFrame); 111 Invalidate(_CursorFrame()); 112 } 113 } 114 WriteUnlock(); 115 } 116 } 117 118 // GetCursorPosition 119 BPoint 120 HWInterface::GetCursorPosition() 121 { 122 BPoint location; 123 if (ReadLock()) { 124 location = fCursorLocation; 125 ReadUnlock(); 126 } 127 return location; 128 } 129 130 // DrawingBuffer 131 RenderingBuffer* 132 HWInterface::DrawingBuffer() const 133 { 134 if (IsDoubleBuffered()) 135 return BackBuffer(); 136 return FrontBuffer(); 137 } 138 139 // IsDoubleBuffered 140 bool 141 HWInterface::IsDoubleBuffered() const 142 { 143 return fDoubleBuffered; 144 } 145 146 // Invalidate 147 // * the object needs to be already locked! 148 status_t 149 HWInterface::Invalidate(const BRect& frame) 150 { 151 if (IsDoubleBuffered()) { 152 return CopyBackToFront(frame); 153 154 // TODO: the remaining problem is the immediate wake up of the 155 // thread carrying out the updates, when I enable it, there 156 // seems to be a deadlock, but I didn't figure it out yet. 157 // Maybe the same bug is there without the wakeup, only, triggered 158 // less often.... scarry, huh? 159 /* if (frame.IsValid()) { 160 fUpdateExecutor->AddRect(frame); 161 return B_OK; 162 } 163 return B_BAD_VALUE;*/ 164 } else { 165 // _DrawCursor(frame); 166 } 167 return B_OK; 168 } 169 170 // CopyBackToFront 171 // * the object must already be locked! 172 status_t 173 HWInterface::CopyBackToFront(const BRect& frame) 174 { 175 RenderingBuffer* frontBuffer = FrontBuffer(); 176 RenderingBuffer* backBuffer = BackBuffer(); 177 178 if (!backBuffer || !frontBuffer) 179 return B_NO_INIT; 180 181 // we need to mess with the area, but it is const 182 BRect area(frame); 183 BRect bufferClip(backBuffer->Bounds()); 184 185 if (area.IsValid() && area.Intersects(bufferClip)) { 186 187 // make sure we don't copy out of bounds 188 area = bufferClip & area; 189 190 uint32 srcBPR = backBuffer->BytesPerRow(); 191 uint8* src = (uint8*)backBuffer->Bits(); 192 193 // convert to integer coordinates 194 int32 left = (int32)floorf(area.left); 195 int32 top = (int32)floorf(area.top); 196 int32 right = (int32)ceilf(area.right); 197 int32 bottom = (int32)ceilf(area.bottom); 198 199 // offset to left top pixel in source buffer (always B_RGBA32) 200 src += top * srcBPR + left * 4; 201 202 _CopyToFront(src, srcBPR, left, top, right, bottom); 203 _DrawCursor(area); 204 205 return B_OK; 206 } 207 return B_BAD_VALUE; 208 } 209 210 // HideSoftwareCursor 211 void 212 HWInterface::HideSoftwareCursor(const BRect& area) 213 { 214 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { 215 BRect backupArea(fCursorAreaBackup->left, 216 fCursorAreaBackup->top, 217 fCursorAreaBackup->right, 218 fCursorAreaBackup->bottom); 219 if (area.Intersects(backupArea)) { 220 //printf("HideSoftwareCursor(BRect(%.1f, %.1f, %.1f, %.1f))\n", area.left, area.top, area.right, area.bottom); 221 //backupArea.PrintToStream(); 222 _RestoreCursorArea(); 223 } 224 } 225 } 226 227 // HideSoftwareCursor 228 void 229 HWInterface::HideSoftwareCursor() 230 { 231 //printf("HideSoftwareCursor()\n"); 232 _RestoreCursorArea(); 233 } 234 235 // ShowSoftwareCursor 236 void 237 HWInterface::ShowSoftwareCursor() 238 { 239 if (fCursorAreaBackup && fCursorAreaBackup->cursor_hidden) { 240 //printf("ShowSoftwareCursor()\n"); 241 _DrawCursor(_CursorFrame()); 242 } 243 } 244 245 246 // _DrawCursor 247 // * default implementation, can be used as fallback or for 248 // software cursor 249 // * area is where we potentially draw the cursor, the cursor 250 // might be somewhere else, in which case this function does nothing 251 void 252 HWInterface::_DrawCursor(BRect area) const 253 { 254 RenderingBuffer* backBuffer = DrawingBuffer(); 255 if (!backBuffer || !area.IsValid()) 256 return; 257 258 BRect cf = _CursorFrame(); 259 260 // make sure we don't copy out of bounds 261 area = backBuffer->Bounds() & area; 262 263 if (cf.IsValid() && area.Intersects(cf)) { 264 // clip to common area 265 area = area & cf; 266 267 int32 left = (int32)area.left; 268 int32 top = (int32)area.top; 269 int32 right = (int32)area.right; 270 int32 bottom = (int32)area.bottom; 271 int32 width = right - left + 1; 272 int32 height = bottom - top + 1; 273 274 // make a bitmap from the backbuffer 275 // that has the cursor blended on top of it 276 277 // blending buffer 278 uint8* buffer = new uint8[width * height * 4]; 279 280 // offset into back buffer 281 uint8* src = (uint8*)backBuffer->Bits(); 282 uint32 srcBPR = backBuffer->BytesPerRow(); 283 src += top * srcBPR + left * 4; 284 285 // offset into cursor bitmap 286 uint8* crs = (uint8*)fCursor->Bits(); 287 uint32 crsBPR = fCursor->BytesPerRow(); 288 // since area is clipped to cf, 289 // the diff between top and cf.top is always positive, 290 // same for diff between left and cf.left 291 crs += (top - (int32)floorf(cf.top)) * crsBPR 292 + (left - (int32)floorf(cf.left)) * 4; 293 294 uint8* dst = buffer; 295 296 if (fCursorAreaBackup && fCursorAreaBackup->buffer) { 297 //printf("backup: BRect(%ld, %ld, %ld, %ld)\n", left, top, right, bottom); 298 fCursorAreaBackup->cursor_hidden = false; 299 // remember which area the backup contains 300 fCursorAreaBackup->left = left; 301 fCursorAreaBackup->top = top; 302 fCursorAreaBackup->right = right; 303 fCursorAreaBackup->bottom = bottom; 304 uint8* bup = fCursorAreaBackup->buffer; 305 uint32 bupBPR = fCursorAreaBackup->bpr; 306 // blending and backup of drawing buffer 307 for (int32 y = top; y <= bottom; y++) { 308 uint8* s = src; 309 uint8* c = crs; 310 uint8* d = dst; 311 uint8* b = bup; 312 for (int32 x = left; x <= right; x++) { 313 // assumes backbuffer alpha = 255 314 // TODO: it appears alpha in cursor is upside down 315 uint8 a = 255 - c[3]; 316 b[0] = s[0]; 317 b[1] = s[1]; 318 b[2] = s[2]; 319 // TODO: unnecessary? 320 b[3] = 255; 321 d[0] = (((s[0] - c[0]) * a) + (c[0] << 8)) >> 8; 322 d[1] = (((s[1] - c[1]) * a) + (c[1] << 8)) >> 8; 323 d[2] = (((s[2] - c[2]) * a) + (c[2] << 8)) >> 8; 324 d[3] = 255; 325 s += 4; 326 c += 4; 327 d += 4; 328 b += 4; 329 } 330 crs += crsBPR; 331 src += srcBPR; 332 dst += width * 4; 333 bup += bupBPR; 334 } 335 } else { 336 // blending 337 for (int32 y = top; y <= bottom; y++) { 338 uint8* s = src; 339 uint8* c = crs; 340 uint8* d = dst; 341 for (int32 x = left; x <= right; x++) { 342 // assumes backbuffer alpha = 255 343 // TODO: it appears alpha in cursor is upside down 344 uint8 a = 255 - c[3]; 345 d[0] = (((s[0] - c[0]) * a) + (c[0] << 8)) >> 8; 346 d[1] = (((s[1] - c[1]) * a) + (c[1] << 8)) >> 8; 347 d[2] = (((s[2] - c[2]) * a) + (c[2] << 8)) >> 8; 348 d[3] = 255; 349 s += 4; 350 c += 4; 351 d += 4; 352 } 353 crs += crsBPR; 354 src += srcBPR; 355 dst += width * 4; 356 } 357 } 358 359 // copy result to front buffer 360 _CopyToFront(buffer, width * 4, left, top, right, bottom); 361 362 delete[] buffer; 363 } 364 } 365 366 /* 367 // gfxcpy 368 inline 369 void 370 gfxcpy(uint8* dst, uint8* src, int32 numBytes) 371 { 372 uint64* d64 = (uint64*)dst; 373 uint64* s64 = (uint64*)src; 374 int32 numBytesBegin = numBytes; 375 while (numBytes >= 32) { 376 *d64++ = *s64++; 377 *d64++ = *s64++; 378 *d64++ = *s64++; 379 *d64++ = *s64++; 380 numBytes -= 32; 381 } 382 while (numBytes >= 16) { 383 *d64++ = *s64++; 384 *d64++ = *s64++; 385 numBytes -= 16; 386 } 387 while (numBytes >= 8) { 388 *d64++ = *s64++; 389 numBytes -= 8; 390 } 391 if (numBytes > 0) { 392 // update original pointers 393 dst += numBytesBegin - numBytes; 394 src += numBytesBegin - numBytes; 395 numBytesBegin = numBytes; 396 397 uint32* d32 = (uint32*)dst; 398 uint32* s32 = (uint32*)src; 399 while (numBytes >= 4) { 400 *d32++ = *s32++; 401 numBytes -= 4; 402 } 403 // update original pointers 404 dst += numBytesBegin - numBytes; 405 src += numBytesBegin - numBytes; 406 407 while (numBytes > 0) { 408 *dst++ = *src++; 409 numBytes--; 410 } 411 } 412 }*/ 413 414 // gfxcpy32 415 // * numBytes is expected to be a multiple of 4 416 inline 417 void 418 gfxcpy32(uint8* dst, uint8* src, int32 numBytes) 419 { 420 uint64* d64 = (uint64*)dst; 421 uint64* s64 = (uint64*)src; 422 int32 numBytesStart = numBytes; 423 while (numBytes >= 32) { 424 *d64++ = *s64++; 425 *d64++ = *s64++; 426 *d64++ = *s64++; 427 *d64++ = *s64++; 428 numBytes -= 32; 429 } 430 if (numBytes >= 16) { 431 *d64++ = *s64++; 432 *d64++ = *s64++; 433 numBytes -= 16; 434 } 435 if (numBytes >= 8) { 436 *d64++ = *s64++; 437 numBytes -= 8; 438 } 439 if (numBytes == 4) { 440 uint32* d32 = (uint32*)(dst + numBytesStart - numBytes); 441 uint32* s32 = (uint32*)(src + numBytesStart - numBytes); 442 *d32 = *s32; 443 } 444 } 445 446 // _CopyToFront 447 // 448 // * source is assumed to be already at the right offset 449 // * source is assumed to be in B_RGBA32 format 450 // * location in front buffer is calculated 451 // * conversion from B_RGBA32 to format of front buffer is taken care of 452 void 453 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, 454 int32 x, int32 y, 455 int32 right, int32 bottom) const 456 { 457 RenderingBuffer* frontBuffer = FrontBuffer(); 458 459 uint8* dst = (uint8*)frontBuffer->Bits(); 460 uint32 dstBPR = frontBuffer->BytesPerRow(); 461 462 // transfer, handle colorspace conversion 463 switch (frontBuffer->ColorSpace()) { 464 case B_RGB32: 465 case B_RGBA32: { 466 int32 bytes = (right - x + 1) * 4; 467 468 if (bytes > 0) { 469 // offset to left top pixel in dest buffer 470 dst += y * dstBPR + x * 4; 471 // copy 472 for (; y <= bottom; y++) { 473 #ifndef __HAIKU__ 474 memcpy(dst, src, bytes); 475 #else 476 // bytes is guaranteed to be multiple of 4 477 gfxcpy32(dst, src, bytes); 478 #endif // __HAIKU__ 479 dst += dstBPR; 480 src += srcBPR; 481 } 482 } else 483 printf("nothing to copy\n"); 484 break; 485 } 486 // NOTE: on R5, B_RGB24 bitmaps are not supported by DrawBitmap() 487 case B_RGB24: { 488 // offset to left top pixel in dest buffer 489 dst += y * dstBPR + x * 3; 490 int32 left = x; 491 // copy 492 for (; y <= bottom; y++) { 493 uint8* srcHandle = src; 494 uint8* dstHandle = dst; 495 for (x = left; x <= right; x++) { 496 dstHandle[0] = srcHandle[0]; 497 dstHandle[1] = srcHandle[1]; 498 dstHandle[2] = srcHandle[2]; 499 dstHandle += 3; 500 srcHandle += 4; 501 } 502 dst += dstBPR; 503 src += srcBPR; 504 } 505 break; 506 } 507 case B_RGB16: { 508 // offset to left top pixel in dest buffer 509 dst += y * dstBPR + x * 2; 510 int32 left = x; 511 // copy 512 // TODO: assumes BGR order, does this work on big endian as well? 513 for (; y <= bottom; y++) { 514 uint8* srcHandle = src; 515 uint16* dstHandle = (uint16*)dst; 516 for (x = left; x <= right; x++) { 517 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8) | 518 ((srcHandle[1] & 0xfc) << 3) | 519 (srcHandle[0] >> 3)); 520 dstHandle ++; 521 srcHandle += 4; 522 } 523 dst += dstBPR; 524 src += srcBPR; 525 } 526 break; 527 } 528 case B_RGB15: { 529 // offset to left top pixel in dest buffer 530 dst += y * dstBPR + x * 2; 531 int32 left = x; 532 // copy 533 // TODO: assumes BGR order, does this work on big endian as well? 534 for (; y <= bottom; y++) { 535 uint8* srcHandle = src; 536 uint16* dstHandle = (uint16*)dst; 537 for (x = left; x <= right; x++) { 538 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7) | 539 ((srcHandle[1] & 0xf8) << 2) | 540 (srcHandle[0] >> 3)); 541 dstHandle ++; 542 srcHandle += 4; 543 } 544 dst += dstBPR; 545 src += srcBPR; 546 } 547 break; 548 } 549 case B_CMAP8: { 550 const color_map *colorMap = SystemColorMap(); 551 // offset to left top pixel in dest buffer 552 dst += y * dstBPR + x; 553 int32 left = x; 554 uint16 index; 555 // copy 556 // TODO: assumes BGR order again 557 for (; y <= bottom; y++) { 558 uint8* srcHandle = src; 559 uint8* dstHandle = dst; 560 for (x = left; x <= right; x++) { 561 index = ((srcHandle[2] & 0xf8) << 7) | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3); 562 *dstHandle = colorMap->index_map[index]; 563 dstHandle ++; 564 srcHandle += 4; 565 } 566 dst += dstBPR; 567 src += srcBPR; 568 } 569 570 break; 571 } 572 case B_GRAY8: { 573 // offset to left top pixel in dest buffer 574 dst += y * dstBPR + x; 575 int32 left = x; 576 // copy 577 // TODO: assumes BGR order, does this work on big endian as well? 578 for (; y <= bottom; y++) { 579 uint8* srcHandle = src; 580 uint8* dstHandle = dst; 581 for (x = left; x <= right; x++) { 582 *dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1] + 116 * srcHandle[0]) / 1024; 583 dstHandle ++; 584 srcHandle += 4; 585 } 586 dst += dstBPR; 587 src += srcBPR; 588 } 589 break; 590 } 591 default: 592 fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported front buffer format!\n"); 593 break; 594 } 595 } 596 597 598 // _CursorFrame 599 // 600 // PRE: the object must be locked 601 BRect 602 HWInterface::_CursorFrame() const 603 { 604 BRect frame(0.0, 0.0, -1.0, -1.0); 605 if (fCursor && fCursorVisible) { 606 frame = fCursor->Bounds(); 607 frame.OffsetTo(fCursorLocation - fCursor->GetHotSpot()); 608 } 609 return frame; 610 } 611 612 // _RestoreCursorArea 613 void 614 HWInterface::_RestoreCursorArea() const 615 { 616 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) { 617 //printf("restore\n"); 618 _CopyToFront(fCursorAreaBackup->buffer, 619 fCursorAreaBackup->bpr, 620 fCursorAreaBackup->left, 621 fCursorAreaBackup->top, 622 fCursorAreaBackup->right, 623 fCursorAreaBackup->bottom); 624 625 fCursorAreaBackup->cursor_hidden = true; 626 } 627 } 628 629 630 631