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