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