xref: /haiku/src/servers/app/drawing/AlphaMask.cpp (revision 610f99c838cb661ff85377789ffd3ad4ff672a08)
1 /*
2  * Copyright 2014-2015, Haiku, Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Adrien Destugues <pulkomandy@pulkomandy.tk>
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *		Julian Harnath <julian.harnath@rwth-aachen.de>
9  */
10 
11 
12 #include "AlphaMask.h"
13 
14 #include "AlphaMaskCache.h"
15 #include "BitmapHWInterface.h"
16 #include "BitmapManager.h"
17 #include "Canvas.h"
18 #include "DrawingEngine.h"
19 #include "PictureBoundingBoxPlayer.h"
20 #include "ServerBitmap.h"
21 #include "ServerPicture.h"
22 #include "Shape.h"
23 #include "ShapePrivate.h"
24 
25 #include <AutoLocker.h>
26 
27 
28 // #pragma mark - AlphaMask
29 
30 
31 AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse)
32 	:
33 	fPreviousMask(previousMask),
34 	fBounds(),
35 	fClippedToCanvas(true),
36 	fCanvasOrigin(),
37 	fCanvasBounds(),
38 	fInverse(inverse),
39 	fBackgroundOpacity(0),
40 	fNextMaskCount(0),
41 	fInCache(false),
42 	fIndirectCacheReferences(0),
43 	fBits(NULL),
44 	fBuffer(),
45 	fMask(),
46 	fScanline(fMask)
47 {
48 	recursive_lock_init(&fLock, "AlphaMask");
49 
50 	if (previousMask != NULL)
51 		atomic_add(&previousMask->fNextMaskCount, 1);
52 
53 	_SetOutsideOpacity();
54 }
55 
56 
57 AlphaMask::AlphaMask(AlphaMask* previousMask, AlphaMask* other)
58 	:
59 	fPreviousMask(previousMask),
60 	fBounds(other->fBounds),
61 	fClippedToCanvas(other->fClippedToCanvas),
62 	fCanvasOrigin(other->fCanvasOrigin),
63 	fCanvasBounds(other->fCanvasBounds),
64 	fInverse(other->fInverse),
65 	fBackgroundOpacity(other->fBackgroundOpacity),
66 	fNextMaskCount(0),
67 	fInCache(false),
68 	fIndirectCacheReferences(0),
69 	fBits(other->fBits),
70 	fBuffer(other->fBuffer),
71 	fMask(other->fMask),
72 	fScanline(fMask)
73 {
74 	recursive_lock_init(&fLock, "AlphaMask");
75 
76 	fMask.attach(fBuffer);
77 
78 	if (previousMask != NULL)
79 		atomic_add(&previousMask->fNextMaskCount, 1);
80 
81 	_SetOutsideOpacity();
82 }
83 
84 
85 AlphaMask::AlphaMask(uint8 backgroundOpacity)
86 	:
87 	fPreviousMask(),
88 	fBounds(),
89 	fClippedToCanvas(true),
90 	fCanvasOrigin(),
91 	fCanvasBounds(),
92 	fInverse(false),
93 	fBackgroundOpacity(backgroundOpacity),
94 	fNextMaskCount(0),
95 	fInCache(false),
96 	fIndirectCacheReferences(0),
97 	fBits(NULL),
98 	fBuffer(),
99 	fMask(),
100 	fScanline(fMask)
101 {
102 	recursive_lock_init(&fLock, "AlphaMask");
103 
104 	_SetOutsideOpacity();
105 }
106 
107 
108 AlphaMask::~AlphaMask()
109 {
110 	if (fPreviousMask.IsSet())
111 		atomic_add(&fPreviousMask->fNextMaskCount, -1);
112 
113 	recursive_lock_destroy(&fLock);
114 }
115 
116 
117 IntPoint
118 AlphaMask::SetCanvasGeometry(IntPoint origin, IntRect bounds)
119 {
120 	RecursiveLocker locker(fLock);
121 
122 	if (origin == fCanvasOrigin && bounds.Width() == fCanvasBounds.Width()
123 		&& bounds.Height() == fCanvasBounds.Height())
124 		return fCanvasOrigin;
125 
126 	IntPoint oldOrigin = fCanvasOrigin;
127 	fCanvasOrigin = origin;
128 	IntRect oldBounds = fCanvasBounds;
129 	fCanvasBounds = IntRect(0, 0, bounds.Width(), bounds.Height());
130 
131 	if (fPreviousMask != NULL)
132 		fPreviousMask->SetCanvasGeometry(origin, bounds);
133 
134 	if (fClippedToCanvas && (fCanvasBounds.Width() > oldBounds.Width()
135 		|| fCanvasBounds.Height() > oldBounds.Height())) {
136 		// The canvas is now larger than before and we previously
137 		// drew the alpha mask clipped to the (old) bounds of the
138 		// canvas. So we now have to redraw the alpha mask with the
139 		// new size.
140 		_Generate();
141 	}
142 
143 	_AttachMaskToBuffer();
144 
145 	return oldOrigin;
146 }
147 
148 
149 size_t
150 AlphaMask::BitmapSize() const
151 {
152 	return fBits->BitsLength();
153 }
154 
155 
156 ServerBitmap*
157 AlphaMask::_CreateTemporaryBitmap(BRect bounds) const
158 {
159 	BReference<UtilityBitmap> bitmap(new(std::nothrow) UtilityBitmap(bounds,
160 		B_RGBA32, 0), true);
161 	if (bitmap == NULL)
162 		return NULL;
163 
164 	if (!bitmap->IsValid())
165 		return NULL;
166 
167 	memset(bitmap->Bits(), fBackgroundOpacity, bitmap->BitsLength());
168 
169 	return bitmap.Detach();
170 }
171 
172 
173 void
174 AlphaMask::_Generate()
175 {
176 	RecursiveLocker locker(fLock);
177 	RecursiveLocker previousLocker;
178 	if (fPreviousMask != NULL)
179 		previousLocker.SetTo(fPreviousMask->fLock, false);
180 
181 	ServerBitmap* const bitmap = _RenderSource(fCanvasBounds);
182 	BReference<ServerBitmap> bitmapRef(bitmap, true);
183 	if (bitmap == NULL) {
184 		_SetNoClipping();
185 		return;
186 	}
187 
188 	fBits.SetTo(new(std::nothrow) UtilityBitmap(fBounds, B_GRAY8, 0), true);
189 	if (fBits == NULL)
190 		return;
191 
192 	const int32 width = fBits->Width();
193 	const int32 height = fBits->Height();
194 	uint8* source = bitmap->Bits();
195 	uint8* destination = fBits->Bits();
196 	uint32 numPixels = width * height;
197 
198 	if (fPreviousMask != NULL) {
199 		uint8 previousOutsideOpacity = fPreviousMask->OutsideOpacity();
200 
201 		if (fPreviousMask->fBounds.Intersects(fBounds)) {
202 			IntRect previousBounds(fBounds.OffsetByCopy(
203 				-fPreviousMask->fBounds.left, -fPreviousMask->fBounds.top));
204 			if (previousBounds.right > fPreviousMask->fBounds.Width())
205 				previousBounds.right = fPreviousMask->fBounds.Width();
206 			if (previousBounds.bottom > fPreviousMask->fBounds.Height())
207 				previousBounds.bottom = fPreviousMask->fBounds.Height();
208 
209 			int32 y = previousBounds.top;
210 
211 			for (; y < 0; y++) {
212 				for (int32 x = 0; x < width; x++) {
213 					*destination = (fInverse ? 255 - source[3] : source[3])
214 						* previousOutsideOpacity / 255;
215 					destination++;
216 					source += 4;
217 				}
218 			}
219 
220 			for (; y <= previousBounds.bottom; y++) {
221 				int32 x = previousBounds.left;
222 				for (; x < 0; x++) {
223 					*destination = (fInverse ? 255 - source[3] : source[3])
224 						* previousOutsideOpacity / 255;
225 					destination++;
226 					source += 4;
227 				}
228 				uint8* previousRow = fPreviousMask->fBuffer.row_ptr(y);
229 				for (; x <= previousBounds.right; x++) {
230 					uint8 sourceAlpha = fInverse ? 255 - source[3] : source[3];
231 					*destination = sourceAlpha * previousRow[x] / 255;
232 					destination++;
233 					source += 4;
234 				}
235 				for (; x < previousBounds.left + width; x++) {
236 					*destination = (fInverse ? 255 - source[3] : source[3])
237 						* previousOutsideOpacity / 255;
238 					destination++;
239 					source += 4;
240 				}
241 			}
242 
243 			for (; y < previousBounds.top + height; y++) {
244 				for (int32 x = 0; x < width; x++) {
245 					*destination = (fInverse ? 255 - source[3] : source[3])
246 						* previousOutsideOpacity / 255;
247 					destination++;
248 					source += 4;
249 				}
250 			}
251 
252 		} else {
253 			while (numPixels--) {
254 				*destination = (fInverse ? 255 - source[3] : source[3])
255 					* previousOutsideOpacity / 255;
256 				destination++;
257 				source += 4;
258 			}
259 		}
260 	} else {
261 		while (numPixels--) {
262 			*destination = fInverse ? 255 - source[3] : source[3];
263 			destination++;
264 			source += 4;
265 		}
266 	}
267 
268 	fBuffer.attach(fBits->Bits(), width, height, width);
269 	_AttachMaskToBuffer();
270 
271 	_AddToCache();
272 }
273 
274 
275 void
276 AlphaMask::_SetNoClipping()
277 {
278 	fBuffer.attach(NULL, 0, 0, 0);
279 	_AttachMaskToBuffer();
280 }
281 
282 
283 const IntRect&
284 AlphaMask::_PreviousMaskBounds() const
285 {
286 	return fPreviousMask->fBounds;
287 }
288 
289 
290 void
291 AlphaMask::_AttachMaskToBuffer()
292 {
293 	const IntPoint maskOffset = _Offset();
294 	const int32 offsetX = fBounds.left + maskOffset.x + fCanvasOrigin.x;
295 	const int32 offsetY = fBounds.top + maskOffset.y + fCanvasOrigin.y;
296 
297 	fMask.attach(fBuffer, offsetX, offsetY, fOutsideOpacity);
298 }
299 
300 
301 void
302 AlphaMask::_SetOutsideOpacity()
303 {
304 	fOutsideOpacity = fInverse ? 255 - fBackgroundOpacity
305 		: fBackgroundOpacity;
306 
307 	if (fPreviousMask != NULL) {
308 		fOutsideOpacity = fOutsideOpacity * fPreviousMask->OutsideOpacity()
309 			/ 255;
310 	}
311 }
312 
313 
314 // #pragma mark - UniformAlphaMask
315 
316 
317 UniformAlphaMask::UniformAlphaMask(uint8 opacity)
318 	:
319 	AlphaMask(opacity)
320 {
321 	fBounds.Set(0, 0, 0, 0);
322 	_SetNoClipping();
323 }
324 
325 
326 ServerBitmap*
327 UniformAlphaMask::_RenderSource(const IntRect&)
328 {
329 	return NULL;
330 }
331 
332 
333 IntPoint
334 UniformAlphaMask::_Offset()
335 {
336 	return IntPoint(0, 0);
337 }
338 
339 
340 void
341 UniformAlphaMask::_AddToCache()
342 {
343 }
344 
345 
346 // #pragma mark - VectorAlphaMask
347 
348 
349 template<class VectorMaskType>
350 VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
351 	BPoint where, bool inverse)
352 	:
353 	AlphaMask(previousMask, inverse),
354 	fWhere(where)
355 {
356 }
357 
358 
359 template<class VectorMaskType>
360 VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
361 	VectorAlphaMask* other)
362 	:
363 	AlphaMask(previousMask, other),
364 	fWhere(other->fWhere)
365 {
366 }
367 
368 
369 template<class VectorMaskType>
370 ServerBitmap*
371 VectorAlphaMask<VectorMaskType>::_RenderSource(const IntRect& canvasBounds)
372 {
373 	fBounds = static_cast<VectorMaskType*>(this)->DetermineBoundingBox();
374 
375 	if (fBounds.Width() > canvasBounds.Width()
376 		|| fBounds.Height() > canvasBounds.Height()) {
377 		fBounds = fBounds & canvasBounds;
378 		fClippedToCanvas = true;
379 	} else
380 		fClippedToCanvas = false;
381 
382 	if (fPreviousMask != NULL) {
383 		if (IsInverted()) {
384 			if (fPreviousMask->OutsideOpacity() != 0) {
385 				IntRect previousBounds = _PreviousMaskBounds();
386 				if (previousBounds.IsValid())
387 					fBounds = fBounds | previousBounds;
388 			} else
389 				fBounds = _PreviousMaskBounds();
390 			fClippedToCanvas = fClippedToCanvas || fPreviousMask->IsClipped();
391 		} else if (fPreviousMask->OutsideOpacity() == 0)
392 			fBounds = fBounds & _PreviousMaskBounds();
393 	}
394 	if (!fBounds.IsValid())
395 		return NULL;
396 
397 	BReference<ServerBitmap> bitmap(_CreateTemporaryBitmap(fBounds), true);
398 	if (bitmap == NULL)
399 		return NULL;
400 
401 	// Render the picture to the bitmap
402 	BitmapHWInterface interface(bitmap);
403 	ObjectDeleter<DrawingEngine> engine(interface.CreateDrawingEngine());
404 	if (!engine.IsSet())
405 		return NULL;
406 
407 	engine->SetRendererOffset(fBounds.left, fBounds.top);
408 
409 	OffscreenCanvas canvas(engine.Get(),
410 		static_cast<VectorMaskType*>(this)->GetDrawState(), fBounds);
411 
412 	DrawState* const drawState = canvas.CurrentState();
413 	drawState->SetDrawingMode(B_OP_ALPHA);
414 	drawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
415 	drawState->SetDrawingModeLocked(true);
416 	canvas.PushState();
417 
418 	canvas.ResyncDrawState();
419 
420 	if (engine->LockParallelAccess()) {
421 		BRegion clipping;
422 		clipping.Set((clipping_rect)fBounds);
423 		engine->ConstrainClippingRegion(&clipping);
424 		static_cast<VectorMaskType*>(this)->DrawVectors(&canvas);
425 		engine->UnlockParallelAccess();
426 	}
427 
428 	canvas.PopState();
429 
430 	return bitmap.Detach();
431 }
432 
433 
434 template<class VectorMaskType>
435 IntPoint
436 VectorAlphaMask<VectorMaskType>::_Offset()
437 {
438 	return fWhere;
439 }
440 
441 
442 
443 // #pragma mark - PictureAlphaMask
444 
445 
446 PictureAlphaMask::PictureAlphaMask(AlphaMask* previousMask,
447 	ServerPicture* picture, const DrawState& drawState, BPoint where,
448 	bool inverse)
449 	:
450 	VectorAlphaMask<PictureAlphaMask>(previousMask, where, inverse),
451 	fPicture(picture),
452 	fDrawState(new(std::nothrow) DrawState(drawState))
453 {
454 }
455 
456 
457 PictureAlphaMask::~PictureAlphaMask()
458 {
459 }
460 
461 
462 void
463 PictureAlphaMask::DrawVectors(Canvas* canvas)
464 {
465 	fPicture->Play(canvas);
466 }
467 
468 
469 BRect
470 PictureAlphaMask::DetermineBoundingBox() const
471 {
472 	BRect boundingBox;
473 	PictureBoundingBoxPlayer::Play(fPicture, fDrawState.Get(), &boundingBox);
474 
475 	if (!boundingBox.IsValid())
476 		return boundingBox;
477 
478 	// Round up and add an additional 2 pixels on the bottom/right to
479 	// compensate for the various types of rounding used in Painter.
480 	boundingBox.left = floorf(boundingBox.left);
481 	boundingBox.right = ceilf(boundingBox.right) + 2;
482 	boundingBox.top = floorf(boundingBox.top);
483 	boundingBox.bottom = ceilf(boundingBox.bottom) + 2;
484 
485 	return boundingBox;
486 }
487 
488 
489 const DrawState&
490 PictureAlphaMask::GetDrawState() const
491 {
492 	return *fDrawState.Get();
493 }
494 
495 
496 void
497 PictureAlphaMask::_AddToCache()
498 {
499 	// currently not implemented
500 }
501 
502 
503 // #pragma mark - ShapeAlphaMask
504 
505 
506 DrawState* ShapeAlphaMask::fDrawState = NULL;
507 
508 
509 ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask,
510 	const shape_data& shape, BPoint where, bool inverse)
511 	:
512 	VectorAlphaMask<ShapeAlphaMask>(previousMask, where, inverse),
513 	fShape(new(std::nothrow) shape_data(shape), true)
514 {
515 	if (fDrawState == NULL)
516 		fDrawState = new(std::nothrow) DrawState();
517 
518 	fShapeBounds = fShape->DetermineBoundingBox();
519 }
520 
521 
522 ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask,
523 	ShapeAlphaMask* other)
524 	:
525 	VectorAlphaMask<ShapeAlphaMask>(previousMask, other),
526 	fShape(other->fShape),
527 	fShapeBounds(other->fShapeBounds)
528 {
529 }
530 
531 
532 ShapeAlphaMask::~ShapeAlphaMask()
533 {
534 }
535 
536 
537 /* static */ ShapeAlphaMask*
538 ShapeAlphaMask::Create(AlphaMask* previousMask, const shape_data& shape,
539 	BPoint where, bool inverse)
540 {
541 	// Look if we have a suitable cached mask
542 	BReference<ShapeAlphaMask> mask(AlphaMaskCache::Default()->Get(shape,
543 		previousMask, inverse), true);
544 
545 	if (mask == NULL) {
546 		// No cached mask, create new one
547 		mask.SetTo(new(std::nothrow) ShapeAlphaMask(previousMask, shape,
548 			BPoint(0, 0), inverse), true);
549 	} else {
550 		// Create new mask which reuses the parameters and the mask bitmap
551 		// of the cache entry
552 		// TODO: don't make a new mask if the cache entry has no drawstate
553 		// using it anymore, because then we ca just immediately reuse it
554 		RecursiveLocker locker(mask->fLock);
555 		mask.SetTo(new(std::nothrow) ShapeAlphaMask(previousMask, mask), true);
556 	}
557 
558 	return mask.Detach();
559 }
560 
561 
562 void
563 ShapeAlphaMask::DrawVectors(Canvas* canvas)
564 {
565 	canvas->GetDrawingEngine()->DrawShape(fBounds,
566 		fShape->opCount, fShape->opList,
567 		fShape->ptCount, fShape->ptList,
568 		true, BPoint(0, 0), 1.0);
569 }
570 
571 
572 BRect
573 ShapeAlphaMask::DetermineBoundingBox() const
574 {
575 	return fShapeBounds;
576 }
577 
578 
579 const DrawState&
580 ShapeAlphaMask::GetDrawState() const
581 {
582 	return *fDrawState;
583 }
584 
585 
586 void
587 ShapeAlphaMask::_AddToCache()
588 {
589 	AlphaMaskCache::Default()->Put(this);
590 }
591