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