2 * Copyright 2014-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
6 * Adrien Destugues <pulkomandy@pulkomandy.tk>
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Julian Harnath <julian.harnath@rwth-aachen.de>
12 #include "AlphaMask.h"
14 #include "AlphaMaskCache.h"
15 #include "BitmapHWInterface.h"
16 #include "BitmapManager.h"
18 #include "DrawingEngine.h"
19 #include "PictureBoundingBoxPlayer.h"
20 #include "ServerBitmap.h"
21 #include "ServerPicture.h"
23 #include "ShapePrivate.h"
25 #include <AutoLocker.h>
28 // #pragma mark - AlphaMask
31 AlphaMask::AlphaMask(AlphaMask
* previousMask
, bool inverse
)
33 fPreviousMask(previousMask
),
35 fClippedToCanvas(true),
39 fBackgroundOpacity(0),
42 fIndirectCacheReferences(0),
48 if (previousMask
!= NULL
)
49 atomic_add(&previousMask
->fNextMaskCount
, 1);
53 AlphaMask::AlphaMask(AlphaMask
* previousMask
, AlphaMask
* other
)
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
),
64 fIndirectCacheReferences(0),
66 fBuffer(other
->fBuffer
),
70 fMask
.attach(fBuffer
);
72 if (previousMask
!= NULL
)
73 atomic_add(&previousMask
->fNextMaskCount
, 1);
74 fBits
->AcquireReference();
78 AlphaMask::AlphaMask(uint8 backgroundOpacity
)
82 fClippedToCanvas(true),
86 fBackgroundOpacity(backgroundOpacity
),
89 fIndirectCacheReferences(0),
98 AlphaMask::~AlphaMask()
101 fBits
->ReleaseReference();
102 if (fPreviousMask
.Get() != NULL
)
103 atomic_add(&fPreviousMask
->fNextMaskCount
, -1);
108 AlphaMask::SetCanvasGeometry(IntPoint origin
, IntRect bounds
)
110 AutoLocker
<BLocker
> locker(fLock
);
112 if (origin
== fCanvasOrigin
&& bounds
.Width() == fCanvasBounds
.Width()
113 && bounds
.Height() == fCanvasBounds
.Height())
114 return fCanvasOrigin
;
116 IntPoint oldOrigin
= fCanvasOrigin
;
117 fCanvasOrigin
= origin
;
118 IntRect oldBounds
= fCanvasBounds
;
119 fCanvasBounds
= IntRect(0, 0, bounds
.Width(), bounds
.Height());
121 if (fPreviousMask
!= NULL
)
122 fPreviousMask
->SetCanvasGeometry(origin
, bounds
);
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
133 _AttachMaskToBuffer();
140 AlphaMask::BitmapSize() const
142 return fBits
->BitsLength();
147 AlphaMask::_CreateTemporaryBitmap(BRect bounds
) const
149 UtilityBitmap
* bitmap
= new(std::nothrow
) UtilityBitmap(bounds
,
154 if (!bitmap
->IsValid()) {
159 memset(bitmap
->Bits(), fBackgroundOpacity
, bitmap
->BitsLength());
166 AlphaMask::_Generate()
168 AutoLocker
<BLocker
> locker(fLock
);
169 AutoLocker
<BLocker
> previousLocker
;
170 if (fPreviousMask
!= NULL
)
171 previousLocker
.SetTo(fPreviousMask
->fLock
, false);
173 ServerBitmap
* const bitmap
= _RenderSource(fCanvasBounds
);
174 BReference
<ServerBitmap
> bitmapRef(bitmap
, true);
175 if (bitmap
== NULL
) {
181 fBits
->ReleaseReference();
182 fBits
= new(std::nothrow
) UtilityBitmap(fBounds
, B_GRAY8
, 0);
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
;
192 if (fPreviousMask
!= NULL
) {
193 int32 previousStartX
= fBounds
.left
- fPreviousMask
->fBounds
.left
;
194 int32 previousStartY
= fBounds
.top
- fPreviousMask
->fBounds
.top
;
195 if (previousStartX
< 0)
197 if (previousStartY
< 0)
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;
210 while (numPixels
--) {
211 *destination
= fInverse
? 255 - source
[3] : source
[3];
217 fBuffer
.attach(fBits
->Bits(), width
, height
, width
);
218 _AttachMaskToBuffer();
225 AlphaMask::_SetNoClipping()
227 fBuffer
.attach(NULL
, 0, 0, 0);
228 _AttachMaskToBuffer();
233 AlphaMask::_PreviousMaskBounds() const
235 return fPreviousMask
->fBounds
;
240 AlphaMask::_AttachMaskToBuffer()
242 uint8 outsideOpacity
= fInverse
? 255 - fBackgroundOpacity
243 : fBackgroundOpacity
;
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
;
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
;
258 fMask
.attach(fBuffer
, offsetX
, offsetY
, outsideOpacity
);
262 // #pragma mark - UniformAlphaMask
265 UniformAlphaMask::UniformAlphaMask(uint8 opacity
)
269 fBounds
.Set(0, 0, 0, 0);
275 UniformAlphaMask::_RenderSource(const IntRect
&)
282 UniformAlphaMask::_Offset()
284 return IntPoint(0, 0);
289 UniformAlphaMask::_AddToCache()
294 // #pragma mark - VectorAlphaMask
297 template<class VectorMaskType
>
298 VectorAlphaMask
<VectorMaskType
>::VectorAlphaMask(AlphaMask
* previousMask
,
299 BPoint where
, bool inverse
)
301 AlphaMask(previousMask
, inverse
),
307 template<class VectorMaskType
>
308 VectorAlphaMask
<VectorMaskType
>::VectorAlphaMask(AlphaMask
* previousMask
,
309 VectorAlphaMask
* other
)
311 AlphaMask(previousMask
, other
),
312 fWhere(other
->fWhere
)
317 template<class VectorMaskType
>
319 VectorAlphaMask
<VectorMaskType
>::_RenderSource(const IntRect
& canvasBounds
)
321 fBounds
= static_cast<VectorMaskType
*>(this)->DetermineBoundingBox();
323 if (fBounds
.Width() > canvasBounds
.Width()
324 || fBounds
.Height() > canvasBounds
.Height()) {
325 fBounds
= fBounds
& canvasBounds
;
326 fClippedToCanvas
= true;
328 fClippedToCanvas
= false;
330 if (fPreviousMask
!= NULL
)
331 fBounds
= fBounds
& _PreviousMaskBounds();
332 if (!fBounds
.IsValid())
335 ServerBitmap
* bitmap
= _CreateTemporaryBitmap(fBounds
);
339 // Render the picture to the bitmap
340 BitmapHWInterface
interface(bitmap
);
341 DrawingEngine
* engine
= interface
.CreateDrawingEngine();
342 if (engine
== NULL
) {
343 bitmap
->ReleaseReference();
346 engine
->SetRendererOffset(fBounds
.left
, fBounds
.top
);
348 OffscreenCanvas
canvas(engine
,
349 static_cast<VectorMaskType
*>(this)->GetDrawState(), fBounds
);
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);
357 canvas
.ResyncDrawState();
359 if (engine
->LockParallelAccess()) {
361 clipping
.Set((clipping_rect
)fBounds
);
362 engine
->ConstrainClippingRegion(&clipping
);
363 static_cast<VectorMaskType
*>(this)->DrawVectors(&canvas
);
364 engine
->UnlockParallelAccess();
374 template<class VectorMaskType
>
376 VectorAlphaMask
<VectorMaskType
>::_Offset()
383 // #pragma mark - PictureAlphaMask
386 PictureAlphaMask::PictureAlphaMask(AlphaMask
* previousMask
,
387 ServerPicture
* picture
, const DrawState
& drawState
, BPoint where
,
390 VectorAlphaMask
<PictureAlphaMask
>(previousMask
, where
, inverse
),
392 fDrawState(new(std::nothrow
) DrawState(drawState
))
397 PictureAlphaMask::~PictureAlphaMask()
404 PictureAlphaMask::DrawVectors(Canvas
* canvas
)
406 fPicture
->Play(canvas
);
411 PictureAlphaMask::DetermineBoundingBox() const
414 PictureBoundingBoxPlayer::Play(fPicture
, fDrawState
, &boundingBox
);
416 if (!boundingBox
.IsValid())
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;
431 PictureAlphaMask::GetDrawState() const
438 PictureAlphaMask::_AddToCache()
440 // currently not implemented
444 // #pragma mark - ShapeAlphaMask
447 DrawState
* ShapeAlphaMask::fDrawState
= NULL
;
450 ShapeAlphaMask::ShapeAlphaMask(AlphaMask
* previousMask
,
451 const shape_data
& shape
, BPoint where
, bool inverse
)
453 VectorAlphaMask
<ShapeAlphaMask
>(previousMask
, where
, inverse
),
454 fShape(new(std::nothrow
) shape_data(shape
))
456 if (fDrawState
== NULL
)
457 fDrawState
= new(std::nothrow
) DrawState();
459 fShapeBounds
= fShape
->DetermineBoundingBox();
463 ShapeAlphaMask::ShapeAlphaMask(AlphaMask
* previousMask
,
464 ShapeAlphaMask
* other
)
466 VectorAlphaMask
<ShapeAlphaMask
>(previousMask
, other
),
467 fShape(other
->fShape
),
468 fShapeBounds(other
->fShapeBounds
)
470 fShape
->AcquireReference();
474 ShapeAlphaMask::~ShapeAlphaMask()
476 fShape
->ReleaseReference();
480 /* static */ ShapeAlphaMask
*
481 ShapeAlphaMask::Create(AlphaMask
* previousMask
, const shape_data
& shape
,
482 BPoint where
, bool inverse
)
484 // Look if we have a suitable cached mask
485 ShapeAlphaMask
* mask
= AlphaMaskCache::Default()->Get(shape
, previousMask
,
489 // No cached mask, create new one
490 mask
= new(std::nothrow
) ShapeAlphaMask(previousMask
, shape
,
491 BPoint(0, 0), inverse
);
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();
508 ShapeAlphaMask::DrawVectors(Canvas
* canvas
)
510 canvas
->GetDrawingEngine()->DrawShape(fBounds
,
511 fShape
->opCount
, fShape
->opList
,
512 fShape
->ptCount
, fShape
->ptList
,
513 true, BPoint(0, 0), 1.0);
518 ShapeAlphaMask::DetermineBoundingBox() const
525 ShapeAlphaMask::GetDrawState() const
532 ShapeAlphaMask::_AddToCache()
534 AlphaMaskCache::Default()->Put(this);