RemoteDesktop: Implement support for BAffineTransform.
[haiku.git] / src / servers / app / drawing / AlphaMask.cpp
blob4393444ee365d099643913c9ce1c4af49d52cbbe
1 /*
2 * Copyright 2014-2015, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Adrien Destugues <pulkomandy@pulkomandy.tk>
7 * Stephan Aßmus <superstippi@gmx.de>
8 * Julian Harnath <julian.harnath@rwth-aachen.de>
9 */
12 #include "AlphaMask.h"
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"
25 #include <AutoLocker.h>
28 // #pragma mark - AlphaMask
31 AlphaMask::AlphaMask(AlphaMask* previousMask, bool inverse)
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)
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),
62 fNextMaskCount(0),
63 fInCache(false),
64 fIndirectCacheReferences(0),
65 fBits(other->fBits),
66 fBuffer(other->fBuffer),
67 fMask(other->fMask),
68 fScanline(fMask)
70 fMask.attach(fBuffer);
72 if (previousMask != NULL)
73 atomic_add(&previousMask->fNextMaskCount, 1);
74 fBits->AcquireReference();
78 AlphaMask::AlphaMask(uint8 backgroundOpacity)
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)
98 AlphaMask::~AlphaMask()
100 if (fBits != NULL)
101 fBits->ReleaseReference();
102 if (fPreviousMask.Get() != NULL)
103 atomic_add(&fPreviousMask->fNextMaskCount, -1);
107 IntPoint
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
129 // new size.
130 _Generate();
133 _AttachMaskToBuffer();
135 return oldOrigin;
139 size_t
140 AlphaMask::BitmapSize() const
142 return fBits->BitsLength();
146 ServerBitmap*
147 AlphaMask::_CreateTemporaryBitmap(BRect bounds) const
149 UtilityBitmap* bitmap = new(std::nothrow) UtilityBitmap(bounds,
150 B_RGBA32, 0);
151 if (bitmap == NULL)
152 return NULL;
154 if (!bitmap->IsValid()) {
155 delete bitmap;
156 return NULL;
159 memset(bitmap->Bits(), fBackgroundOpacity, bitmap->BitsLength());
161 return bitmap;
165 void
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) {
176 _SetNoClipping();
177 return;
180 if (fBits != NULL)
181 fBits->ReleaseReference();
182 fBits = new(std::nothrow) UtilityBitmap(fBounds, B_GRAY8, 0);
183 if (fBits == NULL)
184 return;
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)
196 previousStartX = 0;
197 if (previousStartY < 0)
198 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;
205 destination++;
206 source += 4;
209 } else {
210 while (numPixels--) {
211 *destination = fInverse ? 255 - source[3] : source[3];
212 destination++;
213 source += 4;
217 fBuffer.attach(fBits->Bits(), width, height, width);
218 _AttachMaskToBuffer();
220 _AddToCache();
224 void
225 AlphaMask::_SetNoClipping()
227 fBuffer.attach(NULL, 0, 0, 0);
228 _AttachMaskToBuffer();
232 const IntRect&
233 AlphaMask::_PreviousMaskBounds() const
235 return fPreviousMask->fBounds;
239 void
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)
267 AlphaMask(opacity)
269 fBounds.Set(0, 0, 0, 0);
270 _SetNoClipping();
274 ServerBitmap*
275 UniformAlphaMask::_RenderSource(const IntRect&)
277 return NULL;
281 IntPoint
282 UniformAlphaMask::_Offset()
284 return IntPoint(0, 0);
288 void
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),
302 fWhere(where)
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>
318 ServerBitmap*
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;
327 } else
328 fClippedToCanvas = false;
330 if (fPreviousMask != NULL)
331 fBounds = fBounds & _PreviousMaskBounds();
332 if (!fBounds.IsValid())
333 return NULL;
335 ServerBitmap* bitmap = _CreateTemporaryBitmap(fBounds);
336 if (bitmap == NULL)
337 return NULL;
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;
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);
355 canvas.PushState();
357 canvas.ResyncDrawState();
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();
367 canvas.PopState();
368 delete engine;
370 return bitmap;
374 template<class VectorMaskType>
375 IntPoint
376 VectorAlphaMask<VectorMaskType>::_Offset()
378 return fWhere;
383 // #pragma mark - PictureAlphaMask
386 PictureAlphaMask::PictureAlphaMask(AlphaMask* previousMask,
387 ServerPicture* picture, const DrawState& drawState, BPoint where,
388 bool inverse)
390 VectorAlphaMask<PictureAlphaMask>(previousMask, where, inverse),
391 fPicture(picture),
392 fDrawState(new(std::nothrow) DrawState(drawState))
397 PictureAlphaMask::~PictureAlphaMask()
399 delete fDrawState;
403 void
404 PictureAlphaMask::DrawVectors(Canvas* canvas)
406 fPicture->Play(canvas);
410 BRect
411 PictureAlphaMask::DetermineBoundingBox() const
413 BRect boundingBox;
414 PictureBoundingBoxPlayer::Play(fPicture, fDrawState, &boundingBox);
416 if (!boundingBox.IsValid())
417 return boundingBox;
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;
426 return boundingBox;
430 const DrawState&
431 PictureAlphaMask::GetDrawState() const
433 return *fDrawState;
437 void
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,
486 inverse);
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();
503 return mask;
507 void
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);
517 BRect
518 ShapeAlphaMask::DetermineBoundingBox() const
520 return fShapeBounds;
524 const DrawState&
525 ShapeAlphaMask::GetDrawState() const
527 return *fDrawState;
531 void
532 ShapeAlphaMask::_AddToCache()
534 AlphaMaskCache::Default()->Put(this);