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