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 54 55 AlphaMask::AlphaMask(AlphaMask* previousMask, AlphaMask* other) 56 : 57 fPreviousMask(previousMask), 58 fBounds(other->fBounds), 59 fClippedToCanvas(other->fClippedToCanvas), 60 fCanvasOrigin(other->fCanvasOrigin), 61 fCanvasBounds(other->fCanvasBounds), 62 fInverse(other->fInverse), 63 fBackgroundOpacity(other->fBackgroundOpacity), 64 fNextMaskCount(0), 65 fInCache(false), 66 fIndirectCacheReferences(0), 67 fBits(other->fBits), 68 fBuffer(other->fBuffer), 69 fMask(other->fMask), 70 fScanline(fMask) 71 { 72 recursive_lock_init(&fLock, "AlphaMask"); 73 74 fMask.attach(fBuffer); 75 76 if (previousMask != NULL) 77 atomic_add(&previousMask->fNextMaskCount, 1); 78 } 79 80 81 AlphaMask::AlphaMask(uint8 backgroundOpacity) 82 : 83 fPreviousMask(), 84 fBounds(), 85 fClippedToCanvas(true), 86 fCanvasOrigin(), 87 fCanvasBounds(), 88 fInverse(false), 89 fBackgroundOpacity(backgroundOpacity), 90 fNextMaskCount(0), 91 fInCache(false), 92 fIndirectCacheReferences(0), 93 fBits(NULL), 94 fBuffer(), 95 fMask(), 96 fScanline(fMask) 97 { 98 recursive_lock_init(&fLock, "AlphaMask"); 99 } 100 101 102 AlphaMask::~AlphaMask() 103 { 104 if (fPreviousMask.IsSet()) 105 atomic_add(&fPreviousMask->fNextMaskCount, -1); 106 107 recursive_lock_destroy(&fLock); 108 } 109 110 111 IntPoint 112 AlphaMask::SetCanvasGeometry(IntPoint origin, IntRect bounds) 113 { 114 RecursiveLocker locker(fLock); 115 116 if (origin == fCanvasOrigin && bounds.Width() == fCanvasBounds.Width() 117 && bounds.Height() == fCanvasBounds.Height()) 118 return fCanvasOrigin; 119 120 IntPoint oldOrigin = fCanvasOrigin; 121 fCanvasOrigin = origin; 122 IntRect oldBounds = fCanvasBounds; 123 fCanvasBounds = IntRect(0, 0, bounds.Width(), bounds.Height()); 124 125 if (fPreviousMask != NULL) 126 fPreviousMask->SetCanvasGeometry(origin, bounds); 127 128 if (fClippedToCanvas && (fCanvasBounds.Width() > oldBounds.Width() 129 || fCanvasBounds.Height() > oldBounds.Height())) { 130 // The canvas is now larger than before and we previously 131 // drew the alpha mask clipped to the (old) bounds of the 132 // canvas. So we now have to redraw the alpha mask with the 133 // new size. 134 _Generate(); 135 } 136 137 _AttachMaskToBuffer(); 138 139 return oldOrigin; 140 } 141 142 143 size_t 144 AlphaMask::BitmapSize() const 145 { 146 return fBits->BitsLength(); 147 } 148 149 150 ServerBitmap* 151 AlphaMask::_CreateTemporaryBitmap(BRect bounds) const 152 { 153 BReference<UtilityBitmap> bitmap(new(std::nothrow) UtilityBitmap(bounds, 154 B_RGBA32, 0), true); 155 if (bitmap == NULL) 156 return NULL; 157 158 if (!bitmap->IsValid()) 159 return NULL; 160 161 memset(bitmap->Bits(), fBackgroundOpacity, bitmap->BitsLength()); 162 163 return bitmap.Detach(); 164 } 165 166 167 void 168 AlphaMask::_Generate() 169 { 170 RecursiveLocker locker(fLock); 171 RecursiveLocker previousLocker; 172 if (fPreviousMask != NULL) 173 previousLocker.SetTo(fPreviousMask->fLock, false); 174 175 ServerBitmap* const bitmap = _RenderSource(fCanvasBounds); 176 BReference<ServerBitmap> bitmapRef(bitmap, true); 177 if (bitmap == NULL) { 178 _SetNoClipping(); 179 return; 180 } 181 182 fBits.SetTo(new(std::nothrow) UtilityBitmap(fBounds, B_GRAY8, 0), true); 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 BReference<ServerBitmap> bitmap(_CreateTemporaryBitmap(fBounds), true); 336 if (bitmap == NULL) 337 return NULL; 338 339 // Render the picture to the bitmap 340 BitmapHWInterface interface(bitmap); 341 ObjectDeleter<DrawingEngine> engine(interface.CreateDrawingEngine()); 342 if (!engine.IsSet()) 343 return NULL; 344 345 engine->SetRendererOffset(fBounds.left, fBounds.top); 346 347 OffscreenCanvas canvas(engine.Get(), 348 static_cast<VectorMaskType*>(this)->GetDrawState(), fBounds); 349 350 DrawState* const drawState = canvas.CurrentState(); 351 drawState->SetDrawingMode(B_OP_ALPHA); 352 drawState->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 353 drawState->SetDrawingModeLocked(true); 354 canvas.PushState(); 355 356 canvas.ResyncDrawState(); 357 358 if (engine->LockParallelAccess()) { 359 BRegion clipping; 360 clipping.Set((clipping_rect)fBounds); 361 engine->ConstrainClippingRegion(&clipping); 362 static_cast<VectorMaskType*>(this)->DrawVectors(&canvas); 363 engine->UnlockParallelAccess(); 364 } 365 366 canvas.PopState(); 367 368 return bitmap.Detach(); 369 } 370 371 372 template<class VectorMaskType> 373 IntPoint 374 VectorAlphaMask<VectorMaskType>::_Offset() 375 { 376 return fWhere; 377 } 378 379 380 381 // #pragma mark - PictureAlphaMask 382 383 384 PictureAlphaMask::PictureAlphaMask(AlphaMask* previousMask, 385 ServerPicture* picture, const DrawState& drawState, BPoint where, 386 bool inverse) 387 : 388 VectorAlphaMask<PictureAlphaMask>(previousMask, where, inverse), 389 fPicture(picture), 390 fDrawState(new(std::nothrow) DrawState(drawState)) 391 { 392 } 393 394 395 PictureAlphaMask::~PictureAlphaMask() 396 { 397 } 398 399 400 void 401 PictureAlphaMask::DrawVectors(Canvas* canvas) 402 { 403 fPicture->Play(canvas); 404 } 405 406 407 BRect 408 PictureAlphaMask::DetermineBoundingBox() const 409 { 410 BRect boundingBox; 411 PictureBoundingBoxPlayer::Play(fPicture, fDrawState.Get(), &boundingBox); 412 413 if (!boundingBox.IsValid()) 414 return boundingBox; 415 416 // Round up and add an additional 2 pixels on the bottom/right to 417 // compensate for the various types of rounding used in Painter. 418 boundingBox.left = floorf(boundingBox.left); 419 boundingBox.right = ceilf(boundingBox.right) + 2; 420 boundingBox.top = floorf(boundingBox.top); 421 boundingBox.bottom = ceilf(boundingBox.bottom) + 2; 422 423 return boundingBox; 424 } 425 426 427 const DrawState& 428 PictureAlphaMask::GetDrawState() const 429 { 430 return *fDrawState.Get(); 431 } 432 433 434 void 435 PictureAlphaMask::_AddToCache() 436 { 437 // currently not implemented 438 } 439 440 441 // #pragma mark - ShapeAlphaMask 442 443 444 DrawState* ShapeAlphaMask::fDrawState = NULL; 445 446 447 ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, 448 const shape_data& shape, BPoint where, bool inverse) 449 : 450 VectorAlphaMask<ShapeAlphaMask>(previousMask, where, inverse), 451 fShape(new(std::nothrow) shape_data(shape), true) 452 { 453 if (fDrawState == NULL) 454 fDrawState = new(std::nothrow) DrawState(); 455 456 fShapeBounds = fShape->DetermineBoundingBox(); 457 } 458 459 460 ShapeAlphaMask::ShapeAlphaMask(AlphaMask* previousMask, 461 ShapeAlphaMask* other) 462 : 463 VectorAlphaMask<ShapeAlphaMask>(previousMask, other), 464 fShape(other->fShape), 465 fShapeBounds(other->fShapeBounds) 466 { 467 } 468 469 470 ShapeAlphaMask::~ShapeAlphaMask() 471 { 472 } 473 474 475 /* static */ ShapeAlphaMask* 476 ShapeAlphaMask::Create(AlphaMask* previousMask, const shape_data& shape, 477 BPoint where, bool inverse) 478 { 479 // Look if we have a suitable cached mask 480 BReference<ShapeAlphaMask> mask(AlphaMaskCache::Default()->Get(shape, 481 previousMask, inverse), true); 482 483 if (mask == NULL) { 484 // No cached mask, create new one 485 mask.SetTo(new(std::nothrow) ShapeAlphaMask(previousMask, shape, 486 BPoint(0, 0), inverse), true); 487 } else { 488 // Create new mask which reuses the parameters and the mask bitmap 489 // of the cache entry 490 // TODO: don't make a new mask if the cache entry has no drawstate 491 // using it anymore, because then we ca just immediately reuse it 492 RecursiveLocker locker(mask->fLock); 493 mask.SetTo(new(std::nothrow) ShapeAlphaMask(previousMask, mask), true); 494 } 495 496 return mask.Detach(); 497 } 498 499 500 void 501 ShapeAlphaMask::DrawVectors(Canvas* canvas) 502 { 503 canvas->GetDrawingEngine()->DrawShape(fBounds, 504 fShape->opCount, fShape->opList, 505 fShape->ptCount, fShape->ptList, 506 true, BPoint(0, 0), 1.0); 507 } 508 509 510 BRect 511 ShapeAlphaMask::DetermineBoundingBox() const 512 { 513 return fShapeBounds; 514 } 515 516 517 const DrawState& 518 ShapeAlphaMask::GetDrawState() const 519 { 520 return *fDrawState; 521 } 522 523 524 void 525 ShapeAlphaMask::_AddToCache() 526 { 527 AlphaMaskCache::Default()->Put(this); 528 } 529