vfs: check userland buffers before reading them.
[haiku.git] / src / servers / app / drawing / HWInterface.cpp
blobcf9a67223a074b49d83fa2ae41adce160415d94e
1 /*
2 * Copyright 2005-2012, Haiku.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Stephan Aßmus <superstippi@gmx.de>
7 */
10 #include "HWInterface.h"
12 #include <new>
13 #include <stdio.h>
14 #include <string.h>
15 #include <unistd.h>
17 #include <vesa/vesa_info.h>
19 #include "drawing_support.h"
21 #include "DrawingEngine.h"
22 #include "RenderingBuffer.h"
23 #include "SystemPalette.h"
24 #include "UpdateQueue.h"
27 using std::nothrow;
30 HWInterfaceListener::HWInterfaceListener()
35 HWInterfaceListener::~HWInterfaceListener()
40 // #pragma mark - HWInterface
43 HWInterface::HWInterface(bool doubleBuffered, bool enableUpdateQueue)
45 MultiLocker("hw interface lock"),
46 fCursorAreaBackup(NULL),
47 fFloatingOverlaysLock("floating overlays lock"),
48 fCursor(NULL),
49 fDragBitmap(NULL),
50 fDragBitmapOffset(0, 0),
51 fCursorAndDragBitmap(NULL),
52 fCursorVisible(false),
53 fCursorObscured(false),
54 fHardwareCursorEnabled(false),
55 fCursorLocation(0, 0),
56 fDoubleBuffered(doubleBuffered),
57 fVGADevice(-1),
58 fUpdateExecutor(NULL),
59 fListeners(20)
61 SetAsyncDoubleBuffered(doubleBuffered && enableUpdateQueue);
65 HWInterface::~HWInterface()
67 SetAsyncDoubleBuffered(false);
69 delete fCursorAreaBackup;
71 // The standard cursor doesn't belong us - the drag bitmap might
72 if (fCursor != fCursorAndDragBitmap)
73 delete fCursorAndDragBitmap;
77 status_t
78 HWInterface::Initialize()
80 return MultiLocker::InitCheck();
84 DrawingEngine*
85 HWInterface::CreateDrawingEngine()
87 return new(std::nothrow) DrawingEngine(this);
91 EventStream*
92 HWInterface::CreateEventStream()
94 return NULL;
98 status_t
99 HWInterface::GetAccelerantPath(BString &path)
101 return B_ERROR;
105 status_t
106 HWInterface::GetDriverPath(BString &path)
108 return B_ERROR;
112 status_t
113 HWInterface::GetPreferredMode(display_mode* mode)
115 return B_NOT_SUPPORTED;
119 status_t
120 HWInterface::GetMonitorInfo(monitor_info* info)
122 return B_NOT_SUPPORTED;
126 // #pragma mark -
129 void
130 HWInterface::SetCursor(ServerCursor* cursor)
132 if (!fFloatingOverlaysLock.Lock())
133 return;
135 if (fCursor != cursor) {
136 BRect oldFrame = _CursorFrame();
138 if (fCursorAndDragBitmap == fCursor) {
139 // make sure _AdoptDragBitmap doesn't delete a real cursor
140 fCursorAndDragBitmap = NULL;
143 if (fCursor)
144 fCursor->ReleaseReference();
146 fCursor = cursor;
148 if (fCursor)
149 fCursor->AcquireReference();
151 Invalidate(oldFrame);
153 _AdoptDragBitmap(fDragBitmap, fDragBitmapOffset);
154 Invalidate(_CursorFrame());
156 fFloatingOverlaysLock.Unlock();
160 ServerCursorReference
161 HWInterface::Cursor() const
163 if (!fFloatingOverlaysLock.Lock())
164 return ServerCursorReference(NULL);
166 ServerCursorReference reference(fCursor);
167 fFloatingOverlaysLock.Unlock();
168 return reference;
172 ServerCursorReference
173 HWInterface::CursorAndDragBitmap() const
175 if (!fFloatingOverlaysLock.Lock())
176 return ServerCursorReference(NULL);
178 ServerCursorReference reference(fCursorAndDragBitmap);
179 fFloatingOverlaysLock.Unlock();
180 return reference;
184 void
185 HWInterface::SetCursorVisible(bool visible)
187 if (!fFloatingOverlaysLock.Lock())
188 return;
190 if (fCursorVisible != visible) {
191 // NOTE: _CursorFrame() will
192 // return an invalid rect if
193 // fCursorVisible == false!
194 if (visible) {
195 fCursorVisible = visible;
196 fCursorObscured = false;
197 IntRect r = _CursorFrame();
199 _DrawCursor(r);
200 Invalidate(r);
201 } else {
202 IntRect r = _CursorFrame();
203 fCursorVisible = visible;
205 _RestoreCursorArea();
206 Invalidate(r);
209 fFloatingOverlaysLock.Unlock();
213 bool
214 HWInterface::IsCursorVisible()
216 bool visible = true;
217 if (fFloatingOverlaysLock.Lock()) {
218 visible = fCursorVisible;
219 fFloatingOverlaysLock.Unlock();
221 return visible;
225 void
226 HWInterface::ObscureCursor()
228 if (!fFloatingOverlaysLock.Lock())
229 return;
231 if (!fCursorObscured) {
232 SetCursorVisible(false);
233 fCursorObscured = true;
235 fFloatingOverlaysLock.Unlock();
239 void
240 HWInterface::MoveCursorTo(float x, float y)
242 if (!fFloatingOverlaysLock.Lock())
243 return;
245 BPoint p(x, y);
246 if (p != fCursorLocation) {
247 // unhide cursor if it is obscured only
248 if (fCursorObscured) {
249 SetCursorVisible(true);
251 IntRect oldFrame = _CursorFrame();
252 fCursorLocation = p;
253 if (fCursorVisible) {
254 // Invalidate and _DrawCursor would not draw
255 // anything if the cursor is hidden
256 // (invalid cursor frame), but explicitly
257 // testing for it here saves us some cycles
258 if (fCursorAreaBackup) {
259 // means we have a software cursor which we need to draw
260 _RestoreCursorArea();
261 _DrawCursor(_CursorFrame());
263 IntRect newFrame = _CursorFrame();
264 if (newFrame.Intersects(oldFrame))
265 Invalidate(oldFrame | newFrame);
266 else {
267 Invalidate(oldFrame);
268 Invalidate(newFrame);
272 fFloatingOverlaysLock.Unlock();
276 BPoint
277 HWInterface::CursorPosition()
279 BPoint location;
280 if (fFloatingOverlaysLock.Lock()) {
281 location = fCursorLocation;
282 fFloatingOverlaysLock.Unlock();
284 return location;
288 void
289 HWInterface::SetDragBitmap(const ServerBitmap* bitmap,
290 const BPoint& offsetFromCursor)
292 if (fFloatingOverlaysLock.Lock()) {
293 _AdoptDragBitmap(bitmap, offsetFromCursor);
294 fFloatingOverlaysLock.Unlock();
299 // #pragma mark -
302 RenderingBuffer*
303 HWInterface::DrawingBuffer() const
305 if (IsDoubleBuffered())
306 return BackBuffer();
307 return FrontBuffer();
311 void
312 HWInterface::SetAsyncDoubleBuffered(bool doubleBuffered)
314 if (doubleBuffered) {
315 if (fUpdateExecutor != NULL)
316 return;
317 fUpdateExecutor = new (nothrow) UpdateQueue(this);
318 AddListener(fUpdateExecutor);
319 } else {
320 if (fUpdateExecutor == NULL)
321 return;
322 RemoveListener(fUpdateExecutor);
323 delete fUpdateExecutor;
324 fUpdateExecutor = NULL;
329 bool
330 HWInterface::IsDoubleBuffered() const
332 return fDoubleBuffered;
336 /*! The object needs to be already locked!
338 status_t
339 HWInterface::InvalidateRegion(BRegion& region)
341 int32 count = region.CountRects();
342 for (int32 i = 0; i < count; i++) {
343 status_t result = Invalidate(region.RectAt(i));
344 if (result != B_OK)
345 return result;
348 return B_OK;
352 /*! The object needs to be already locked!
354 status_t
355 HWInterface::Invalidate(const BRect& frame)
357 if (IsDoubleBuffered()) {
358 #if 0
359 // NOTE: The UpdateQueue works perfectly fine, but it screws the
360 // flicker-free-ness of the double buffered rendering. The problem being the
361 // asynchronous nature. The UpdateQueue will transfer regions of the screen
362 // which have been clean at the time we are in this function, but which have
363 // been damaged meanwhile by drawing into them again. All in all, the
364 // UpdateQueue is good for reducing the number of times that the transfer
365 // is performed, and makes it happen during refresh only, but until there
366 // is a smarter way to synchronize this all better, I've disabled it.
367 if (fUpdateExecutor != NULL) {
368 fUpdateExecutor->AddRect(frame);
369 return B_OK;
371 #endif
372 return CopyBackToFront(frame);
374 return B_OK;
378 /*! The object must already be locked!
380 status_t
381 HWInterface::CopyBackToFront(const BRect& frame)
383 RenderingBuffer* frontBuffer = FrontBuffer();
384 RenderingBuffer* backBuffer = BackBuffer();
386 if (!backBuffer || !frontBuffer)
387 return B_NO_INIT;
389 // we need to mess with the area, but it is const
390 IntRect area(frame);
391 IntRect bufferClip(backBuffer->Bounds());
393 if (area.IsValid() && area.Intersects(bufferClip)) {
395 // make sure we don't copy out of bounds
396 area = bufferClip & area;
398 bool cursorLocked = fFloatingOverlaysLock.Lock();
400 BRegion region((BRect)area);
401 if (IsDoubleBuffered())
402 region.Exclude((clipping_rect)_CursorFrame());
404 _CopyBackToFront(region);
406 _DrawCursor(area);
408 if (cursorLocked)
409 fFloatingOverlaysLock.Unlock();
411 return B_OK;
413 return B_BAD_VALUE;
417 void
418 HWInterface::_CopyBackToFront(/*const*/ BRegion& region)
420 RenderingBuffer* backBuffer = BackBuffer();
422 uint32 srcBPR = backBuffer->BytesPerRow();
423 uint8* src = (uint8*)backBuffer->Bits();
425 int32 count = region.CountRects();
426 for (int32 i = 0; i < count; i++) {
427 clipping_rect r = region.RectAtInt(i);
428 // offset to left top pixel in source buffer (always B_RGBA32)
429 uint8* srcOffset = src + r.top * srcBPR + r.left * 4;
430 _CopyToFront(srcOffset, srcBPR, r.left, r.top, r.right, r.bottom);
435 // #pragma mark -
438 overlay_token
439 HWInterface::AcquireOverlayChannel()
441 return NULL;
445 void
446 HWInterface::ReleaseOverlayChannel(overlay_token token)
451 status_t
452 HWInterface::GetOverlayRestrictions(const Overlay* overlay,
453 overlay_restrictions* restrictions)
455 return B_NOT_SUPPORTED;
459 bool
460 HWInterface::CheckOverlayRestrictions(int32 width, int32 height,
461 color_space colorSpace)
463 return false;
467 const overlay_buffer*
468 HWInterface::AllocateOverlayBuffer(int32 width, int32 height, color_space space)
470 return NULL;
474 void
475 HWInterface::FreeOverlayBuffer(const overlay_buffer* buffer)
480 void
481 HWInterface::ConfigureOverlay(Overlay* overlay)
486 void
487 HWInterface::HideOverlay(Overlay* overlay)
492 // #pragma mark -
495 bool
496 HWInterface::HideFloatingOverlays(const BRect& area)
498 if (IsDoubleBuffered())
499 return false;
500 if (!fFloatingOverlaysLock.Lock())
501 return false;
502 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) {
503 BRect backupArea(fCursorAreaBackup->left, fCursorAreaBackup->top,
504 fCursorAreaBackup->right, fCursorAreaBackup->bottom);
505 if (area.Intersects(backupArea)) {
506 _RestoreCursorArea();
507 // do not unlock the cursor lock
508 return true;
511 fFloatingOverlaysLock.Unlock();
512 return false;
516 bool
517 HWInterface::HideFloatingOverlays()
519 if (IsDoubleBuffered())
520 return false;
521 if (!fFloatingOverlaysLock.Lock())
522 return false;
524 _RestoreCursorArea();
525 return true;
529 void
530 HWInterface::ShowFloatingOverlays()
532 if (fCursorAreaBackup && fCursorAreaBackup->cursor_hidden)
533 _DrawCursor(_CursorFrame());
535 fFloatingOverlaysLock.Unlock();
539 // #pragma mark -
542 bool
543 HWInterface::AddListener(HWInterfaceListener* listener)
545 if (listener && !fListeners.HasItem(listener))
546 return fListeners.AddItem(listener);
547 return false;
551 void
552 HWInterface::RemoveListener(HWInterfaceListener* listener)
554 fListeners.RemoveItem(listener);
558 // #pragma mark -
561 /*! Default implementation, can be used as fallback or for software cursor.
562 \param area is where we potentially draw the cursor, the cursor
563 might be somewhere else, in which case this function does nothing
565 void
566 HWInterface::_DrawCursor(IntRect area) const
568 RenderingBuffer* backBuffer = DrawingBuffer();
569 if (!backBuffer || !area.IsValid())
570 return;
572 IntRect cf = _CursorFrame();
574 // make sure we don't copy out of bounds
575 area = backBuffer->Bounds() & area;
577 if (cf.IsValid() && area.Intersects(cf)) {
579 // clip to common area
580 area = area & cf;
582 int32 width = area.right - area.left + 1;
583 int32 height = area.bottom - area.top + 1;
585 // make a bitmap from the backbuffer
586 // that has the cursor blended on top of it
588 // blending buffer
589 uint8* buffer = new uint8[width * height * 4];
590 // TODO: cache this buffer
592 // offset into back buffer
593 uint8* src = (uint8*)backBuffer->Bits();
594 uint32 srcBPR = backBuffer->BytesPerRow();
595 src += area.top * srcBPR + area.left * 4;
597 // offset into cursor bitmap
598 uint8* crs = (uint8*)fCursorAndDragBitmap->Bits();
599 uint32 crsBPR = fCursorAndDragBitmap->BytesPerRow();
600 // since area is clipped to cf,
601 // the diff between area.top and cf.top is always positive,
602 // same for diff between area.left and cf.left
603 crs += (area.top - (int32)floorf(cf.top)) * crsBPR
604 + (area.left - (int32)floorf(cf.left)) * 4;
606 uint8* dst = buffer;
608 if (fCursorAreaBackup && fCursorAreaBackup->buffer
609 && fFloatingOverlaysLock.Lock()) {
610 fCursorAreaBackup->cursor_hidden = false;
611 // remember which area the backup contains
612 fCursorAreaBackup->left = area.left;
613 fCursorAreaBackup->top = area.top;
614 fCursorAreaBackup->right = area.right;
615 fCursorAreaBackup->bottom = area.bottom;
616 uint8* bup = fCursorAreaBackup->buffer;
617 uint32 bupBPR = fCursorAreaBackup->bpr;
619 // blending and backup of drawing buffer
620 for (int32 y = area.top; y <= area.bottom; y++) {
621 uint8* s = src;
622 uint8* c = crs;
623 uint8* d = dst;
624 uint8* b = bup;
626 for (int32 x = area.left; x <= area.right; x++) {
627 *(uint32*)b = *(uint32*)s;
628 // assumes backbuffer alpha = 255
629 // assuming pre-multiplied cursor bitmap
630 int a = 255 - c[3];
631 d[0] = ((int)(b[0] * a + 255) >> 8) + c[0];
632 d[1] = ((int)(b[1] * a + 255) >> 8) + c[1];
633 d[2] = ((int)(b[2] * a + 255) >> 8) + c[2];
635 s += 4;
636 c += 4;
637 d += 4;
638 b += 4;
640 crs += crsBPR;
641 src += srcBPR;
642 dst += width * 4;
643 bup += bupBPR;
645 fFloatingOverlaysLock.Unlock();
646 } else {
647 // blending
648 for (int32 y = area.top; y <= area.bottom; y++) {
649 uint8* s = src;
650 uint8* c = crs;
651 uint8* d = dst;
652 for (int32 x = area.left; x <= area.right; x++) {
653 // assumes backbuffer alpha = 255
654 // assuming pre-multiplied cursor bitmap
655 uint8 a = 255 - c[3];
656 d[0] = ((s[0] * a + 255) >> 8) + c[0];
657 d[1] = ((s[1] * a + 255) >> 8) + c[1];
658 d[2] = ((s[2] * a + 255) >> 8) + c[2];
660 s += 4;
661 c += 4;
662 d += 4;
664 crs += crsBPR;
665 src += srcBPR;
666 dst += width * 4;
669 // copy result to front buffer
670 _CopyToFront(buffer, width * 4, area.left, area.top, area.right,
671 area.bottom);
673 delete[] buffer;
678 /*! - source is assumed to be already at the right offset
679 - source is assumed to be in B_RGBA32 format
680 - location in front buffer is calculated
681 - conversion from B_RGBA32 to format of front buffer is taken care of
683 void
684 HWInterface::_CopyToFront(uint8* src, uint32 srcBPR, int32 x, int32 y,
685 int32 right, int32 bottom) const
687 RenderingBuffer* frontBuffer = FrontBuffer();
689 uint8* dst = (uint8*)frontBuffer->Bits();
690 uint32 dstBPR = frontBuffer->BytesPerRow();
692 // transfer, handle colorspace conversion
693 switch (frontBuffer->ColorSpace()) {
694 case B_RGB32:
695 case B_RGBA32:
697 int32 bytes = (right - x + 1) * 4;
699 if (bytes > 0) {
700 // offset to left top pixel in dest buffer
701 dst += y * dstBPR + x * 4;
702 // copy
703 for (; y <= bottom; y++) {
704 // bytes is guaranteed to be multiple of 4
705 gfxcpy32(dst, src, bytes);
706 dst += dstBPR;
707 src += srcBPR;
710 break;
713 case B_RGB24:
715 // offset to left top pixel in dest buffer
716 dst += y * dstBPR + x * 3;
717 int32 left = x;
718 // copy
719 for (; y <= bottom; y++) {
720 uint8* srcHandle = src;
721 uint8* dstHandle = dst;
722 for (x = left; x <= right; x++) {
723 dstHandle[0] = srcHandle[0];
724 dstHandle[1] = srcHandle[1];
725 dstHandle[2] = srcHandle[2];
726 dstHandle += 3;
727 srcHandle += 4;
729 dst += dstBPR;
730 src += srcBPR;
732 break;
735 case B_RGB16:
737 // offset to left top pixel in dest buffer
738 dst += y * dstBPR + x * 2;
739 int32 left = x;
740 // copy
741 // TODO: assumes BGR order, does this work on big endian as well?
742 for (; y <= bottom; y++) {
743 uint8* srcHandle = src;
744 uint16* dstHandle = (uint16*)dst;
745 for (x = left; x <= right; x++) {
746 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 8)
747 | ((srcHandle[1] & 0xfc) << 3) | (srcHandle[0] >> 3));
748 dstHandle ++;
749 srcHandle += 4;
751 dst += dstBPR;
752 src += srcBPR;
754 break;
757 case B_RGB15:
758 case B_RGBA15:
760 // offset to left top pixel in dest buffer
761 dst += y * dstBPR + x * 2;
762 int32 left = x;
763 // copy
764 // TODO: assumes BGR order, does this work on big endian as well?
765 for (; y <= bottom; y++) {
766 uint8* srcHandle = src;
767 uint16* dstHandle = (uint16*)dst;
768 for (x = left; x <= right; x++) {
769 *dstHandle = (uint16)(((srcHandle[2] & 0xf8) << 7)
770 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3));
771 dstHandle ++;
772 srcHandle += 4;
774 dst += dstBPR;
775 src += srcBPR;
777 break;
780 case B_CMAP8:
782 const color_map *colorMap = SystemColorMap();
783 // offset to left top pixel in dest buffer
784 dst += y * dstBPR + x;
785 int32 left = x;
786 uint16 index;
787 // copy
788 // TODO: assumes BGR order again
789 for (; y <= bottom; y++) {
790 uint8* srcHandle = src;
791 uint8* dstHandle = dst;
792 for (x = left; x <= right; x++) {
793 index = ((srcHandle[2] & 0xf8) << 7)
794 | ((srcHandle[1] & 0xf8) << 2) | (srcHandle[0] >> 3);
795 *dstHandle = colorMap->index_map[index];
796 dstHandle ++;
797 srcHandle += 4;
799 dst += dstBPR;
800 src += srcBPR;
803 break;
806 case B_GRAY8:
807 if (frontBuffer->Width() > dstBPR) {
808 // VGA 16 color grayscale planar mode
809 if (fVGADevice >= 0) {
810 vga_planar_blit_args args;
811 args.source = src;
812 args.source_bytes_per_row = srcBPR;
813 args.left = x;
814 args.top = y;
815 args.right = right;
816 args.bottom = bottom;
817 if (ioctl(fVGADevice, VGA_PLANAR_BLIT, &args, sizeof(args))
818 == 0)
819 break;
822 // Since we cannot set the plane, we do monochrome output
823 dst += y * dstBPR + x / 8;
824 int32 left = x;
826 // TODO: this is awfully slow...
827 // TODO: assumes BGR order
828 for (; y <= bottom; y++) {
829 uint8* srcHandle = src;
830 uint8* dstHandle = dst;
831 uint8 current8 = dstHandle[0];
832 // we store 8 pixels before writing them back
834 for (x = left; x <= right; x++) {
835 uint8 pixel = (308 * srcHandle[2] + 600 * srcHandle[1]
836 + 116 * srcHandle[0]) / 1024;
837 srcHandle += 4;
839 if (pixel > 128)
840 current8 |= 0x80 >> (x & 7);
841 else
842 current8 &= ~(0x80 >> (x & 7));
844 if ((x & 7) == 7) {
845 // last pixel in 8 pixel group
846 dstHandle[0] = current8;
847 dstHandle++;
848 current8 = dstHandle[0];
852 if (x & 7) {
853 // last pixel has not been written yet
854 dstHandle[0] = current8;
856 dst += dstBPR;
857 src += srcBPR;
859 } else {
860 // offset to left top pixel in dest buffer
861 dst += y * dstBPR + x;
862 int32 left = x;
863 // copy
864 // TODO: assumes BGR order, does this work on big endian as well?
865 for (; y <= bottom; y++) {
866 uint8* srcHandle = src;
867 uint8* dstHandle = dst;
868 for (x = left; x <= right; x++) {
869 *dstHandle = (308 * srcHandle[2] + 600 * srcHandle[1]
870 + 116 * srcHandle[0]) / 1024;
871 dstHandle ++;
872 srcHandle += 4;
874 dst += dstBPR;
875 src += srcBPR;
878 break;
880 default:
881 fprintf(stderr, "HWInterface::CopyBackToFront() - unsupported "
882 "front buffer format! (0x%x)\n", frontBuffer->ColorSpace());
883 break;
888 /*! The object must be locked
890 IntRect
891 HWInterface::_CursorFrame() const
893 IntRect frame(0, 0, -1, -1);
894 if (fCursorAndDragBitmap && fCursorVisible && !fHardwareCursorEnabled) {
895 frame = fCursorAndDragBitmap->Bounds();
896 frame.OffsetTo(fCursorLocation - fCursorAndDragBitmap->GetHotSpot());
898 return frame;
902 void
903 HWInterface::_RestoreCursorArea() const
905 if (fCursorAreaBackup && !fCursorAreaBackup->cursor_hidden) {
906 _CopyToFront(fCursorAreaBackup->buffer, fCursorAreaBackup->bpr,
907 fCursorAreaBackup->left, fCursorAreaBackup->top,
908 fCursorAreaBackup->right, fCursorAreaBackup->bottom);
910 fCursorAreaBackup->cursor_hidden = true;
915 void
916 HWInterface::_AdoptDragBitmap(const ServerBitmap* bitmap, const BPoint& offset)
918 // TODO: support other colorspaces/convert bitmap
919 if (bitmap && !(bitmap->ColorSpace() == B_RGB32
920 || bitmap->ColorSpace() == B_RGBA32)) {
921 fprintf(stderr, "HWInterface::_AdoptDragBitmap() - bitmap has yet "
922 "unsupported colorspace\n");
923 return;
926 _RestoreCursorArea();
927 BRect oldCursorFrame = _CursorFrame();
929 if (fCursorAndDragBitmap && fCursorAndDragBitmap != fCursor) {
930 delete fCursorAndDragBitmap;
931 fCursorAndDragBitmap = NULL;
934 if (bitmap != NULL && bitmap->Bounds().Width() > 0 && bitmap->Bounds().Height() > 0) {
935 BRect bitmapFrame = bitmap->Bounds();
936 if (fCursor) {
937 // put bitmap frame and cursor frame into the same
938 // coordinate space (the cursor location is the origin)
939 bitmapFrame.OffsetTo(BPoint(-offset.x, -offset.y));
941 BRect cursorFrame(fCursor->Bounds());
942 BPoint hotspot(fCursor->GetHotSpot());
943 // the hotspot is at the origin
944 cursorFrame.OffsetTo(-hotspot.x, -hotspot.y);
946 BRect combindedBounds = bitmapFrame | cursorFrame;
948 BPoint shift;
949 shift.x = -combindedBounds.left;
950 shift.y = -combindedBounds.top;
952 combindedBounds.OffsetBy(shift);
953 cursorFrame.OffsetBy(shift);
954 bitmapFrame.OffsetBy(shift);
956 fCursorAndDragBitmap = new(std::nothrow) ServerCursor(combindedBounds,
957 bitmap->ColorSpace(), 0, shift);
959 uint8* dst = fCursorAndDragBitmap ? (uint8*)fCursorAndDragBitmap->Bits() : NULL;
960 if (dst == NULL) {
961 // Oops, we could not allocate memory for the drag bitmap.
962 // Let's show the cursor only.
963 delete fCursorAndDragBitmap;
964 fCursorAndDragBitmap = fCursor;
965 } else {
966 // clear the combined buffer
967 uint32 dstBPR = fCursorAndDragBitmap->BytesPerRow();
969 memset(dst, 0, fCursorAndDragBitmap->BitsLength());
971 // put drag bitmap into combined buffer
972 uint8* src = (uint8*)bitmap->Bits();
973 uint32 srcBPR = bitmap->BytesPerRow();
975 dst += (int32)bitmapFrame.top * dstBPR
976 + (int32)bitmapFrame.left * 4;
978 uint32 width = bitmapFrame.IntegerWidth() + 1;
979 uint32 height = bitmapFrame.IntegerHeight() + 1;
981 for (uint32 y = 0; y < height; y++) {
982 memcpy(dst, src, srcBPR);
983 dst += dstBPR;
984 src += srcBPR;
987 // compose cursor into combined buffer
988 dst = (uint8*)fCursorAndDragBitmap->Bits();
989 dst += (int32)cursorFrame.top * dstBPR
990 + (int32)cursorFrame.left * 4;
992 src = (uint8*)fCursor->Bits();
993 srcBPR = fCursor->BytesPerRow();
995 width = cursorFrame.IntegerWidth() + 1;
996 height = cursorFrame.IntegerHeight() + 1;
998 for (uint32 y = 0; y < height; y++) {
999 uint8* d = dst;
1000 uint8* s = src;
1001 for (uint32 x = 0; x < width; x++) {
1002 // takes two semi-transparent pixels
1003 // with unassociated alpha (not pre-multiplied)
1004 // and stays within non-premultiplied color space
1005 if (s[3] > 0) {
1006 if (s[3] == 255) {
1007 d[0] = s[0];
1008 d[1] = s[1];
1009 d[2] = s[2];
1010 d[3] = 255;
1011 } else {
1012 uint8 alphaRest = 255 - s[3];
1013 uint32 alphaTemp
1014 = (65025 - alphaRest * (255 - d[3]));
1015 uint32 alphaDest = d[3] * alphaRest;
1016 uint32 alphaSrc = 255 * s[3];
1017 d[0] = (d[0] * alphaDest + s[0] * alphaSrc)
1018 / alphaTemp;
1019 d[1] = (d[1] * alphaDest + s[1] * alphaSrc)
1020 / alphaTemp;
1021 d[2] = (d[2] * alphaDest + s[2] * alphaSrc)
1022 / alphaTemp;
1023 d[3] = alphaTemp / 255;
1026 // TODO: make sure the alpha is always upside down,
1027 // then it doesn't need to be done when drawing the cursor
1028 // (see _DrawCursor())
1029 // d[3] = 255 - d[3];
1030 d += 4;
1031 s += 4;
1033 dst += dstBPR;
1034 src += srcBPR;
1037 // handle pre-multiplication with alpha
1038 // for faster compositing during cursor drawing
1039 width = combindedBounds.IntegerWidth() + 1;
1040 height = combindedBounds.IntegerHeight() + 1;
1042 dst = (uint8*)fCursorAndDragBitmap->Bits();
1044 for (uint32 y = 0; y < height; y++) {
1045 uint8* d = dst;
1046 for (uint32 x = 0; x < width; x++) {
1047 d[0] = (d[0] * d[3]) >> 8;
1048 d[1] = (d[1] * d[3]) >> 8;
1049 d[2] = (d[2] * d[3]) >> 8;
1050 d += 4;
1052 dst += dstBPR;
1055 } else {
1056 fCursorAndDragBitmap = new ServerCursor(bitmap->Bits(),
1057 bitmapFrame.IntegerWidth() + 1, bitmapFrame.IntegerHeight() + 1,
1058 bitmap->ColorSpace());
1059 fCursorAndDragBitmap->SetHotSpot(BPoint(-offset.x, -offset.y));
1061 } else {
1062 fCursorAndDragBitmap = fCursor;
1065 Invalidate(oldCursorFrame);
1067 // NOTE: the EventDispatcher does the reference counting stuff for us
1068 // TODO: You can not simply call Release() on a ServerBitmap like you
1069 // can for a ServerCursor... it could be changed, but there are linking
1070 // troubles with the test environment that need to be solved than.
1071 // if (fDragBitmap)
1072 // fDragBitmap->Release();
1073 fDragBitmap = bitmap;
1074 fDragBitmapOffset = offset;
1075 // if (fDragBitmap)
1076 // fDragBitmap->Acquire();
1078 delete fCursorAreaBackup;
1079 fCursorAreaBackup = NULL;
1081 if (!fCursorAndDragBitmap)
1082 return;
1084 if (fCursorAndDragBitmap && !IsDoubleBuffered()) {
1085 BRect cursorBounds = fCursorAndDragBitmap->Bounds();
1086 fCursorAreaBackup = new buffer_clip(cursorBounds.IntegerWidth() + 1,
1087 cursorBounds.IntegerHeight() + 1);
1089 _DrawCursor(_CursorFrame());
1093 void
1094 HWInterface::_NotifyFrameBufferChanged()
1096 BList listeners(fListeners);
1097 int32 count = listeners.CountItems();
1098 for (int32 i = 0; i < count; i++) {
1099 HWInterfaceListener* listener
1100 = (HWInterfaceListener*)listeners.ItemAtFast(i);
1101 listener->FrameBufferChanged();
1106 void
1107 HWInterface::_NotifyScreenChanged()
1109 BList listeners(fListeners);
1110 int32 count = listeners.CountItems();
1111 for (int32 i = 0; i < count; i++) {
1112 HWInterfaceListener* listener
1113 = (HWInterfaceListener*)listeners.ItemAtFast(i);
1114 listener->ScreenChanged(this);
1119 /*static*/ bool
1120 HWInterface::_IsValidMode(const display_mode& mode)
1122 // TODO: more of those!
1123 if (mode.virtual_width < 320
1124 || mode.virtual_height < 200)
1125 return false;
1127 return true;