xref: /haiku/src/servers/app/drawing/HWInterface.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
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