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
31AlphaMask::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
57AlphaMask::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
85AlphaMask::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
108AlphaMask::~AlphaMask()
109{
110	if (fPreviousMask.IsSet())
111		atomic_add(&fPreviousMask->fNextMaskCount, -1);
112
113	recursive_lock_destroy(&fLock);
114}
115
116
117IntPoint
118AlphaMask::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
149size_t
150AlphaMask::BitmapSize() const
151{
152	return fBits->BitsLength();
153}
154
155
156ServerBitmap*
157AlphaMask::_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
173void
174AlphaMask::_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
275void
276AlphaMask::_SetNoClipping()
277{
278	fBuffer.attach(NULL, 0, 0, 0);
279	_AttachMaskToBuffer();
280}
281
282
283const IntRect&
284AlphaMask::_PreviousMaskBounds() const
285{
286	return fPreviousMask->fBounds;
287}
288
289
290void
291AlphaMask::_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
301void
302AlphaMask::_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
317UniformAlphaMask::UniformAlphaMask(uint8 opacity)
318	:
319	AlphaMask(opacity)
320{
321	fBounds.Set(0, 0, 0, 0);
322	_SetNoClipping();
323}
324
325
326ServerBitmap*
327UniformAlphaMask::_RenderSource(const IntRect&)
328{
329	return NULL;
330}
331
332
333IntPoint
334UniformAlphaMask::_Offset()
335{
336	return IntPoint(0, 0);
337}
338
339
340void
341UniformAlphaMask::_AddToCache()
342{
343}
344
345
346// #pragma mark - VectorAlphaMask
347
348
349template<class VectorMaskType>
350VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
351	BPoint where, bool inverse)
352	:
353	AlphaMask(previousMask, inverse),
354	fWhere(where)
355{
356}
357
358
359template<class VectorMaskType>
360VectorAlphaMask<VectorMaskType>::VectorAlphaMask(AlphaMask* previousMask,
361	VectorAlphaMask* other)
362	:
363	AlphaMask(previousMask, other),
364	fWhere(other->fWhere)
365{
366}
367
368
369template<class VectorMaskType>
370ServerBitmap*
371VectorAlphaMask<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
434template<class VectorMaskType>
435IntPoint
436VectorAlphaMask<VectorMaskType>::_Offset()
437{
438	return fWhere;
439}
440
441
442
443// #pragma mark - PictureAlphaMask
444
445
446PictureAlphaMask::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
457PictureAlphaMask::~PictureAlphaMask()
458{
459}
460
461
462void
463PictureAlphaMask::DrawVectors(Canvas* canvas)
464{
465	fPicture->Play(canvas);
466}
467
468
469BRect
470PictureAlphaMask::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
489const DrawState&
490PictureAlphaMask::GetDrawState() const
491{
492	return *fDrawState.Get();
493}
494
495
496void
497PictureAlphaMask::_AddToCache()
498{
499	// currently not implemented
500}
501
502
503// #pragma mark - ShapeAlphaMask
504
505
506DrawState* ShapeAlphaMask::fDrawState = NULL;
507
508
509ShapeAlphaMask::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
522ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask,
523	ShapeAlphaMask* other)
524	:
525	VectorAlphaMask<ShapeAlphaMask>(previousMask, other),
526	fShape(other->fShape),
527	fShapeBounds(other->fShapeBounds)
528{
529}
530
531
532ShapeAlphaMask::~ShapeAlphaMask()
533{
534}
535
536
537/* static */ ShapeAlphaMask*
538ShapeAlphaMask::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
562void
563ShapeAlphaMask::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
572BRect
573ShapeAlphaMask::DetermineBoundingBox() const
574{
575	return fShapeBounds;
576}
577
578
579const DrawState&
580ShapeAlphaMask::GetDrawState() const
581{
582	return *fDrawState;
583}
584
585
586void
587ShapeAlphaMask::_AddToCache()
588{
589	AlphaMaskCache::Default()->Put(this);
590}
591