FilterNodeSoftware.cpp (123992B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <cmath> 8 #include "DataSurfaceHelpers.h" 9 #include "FilterNodeSoftware.h" 10 #include "2D.h" 11 #include "Tools.h" 12 #include "Blur.h" 13 #include <map> 14 #include "FilterProcessing.h" 15 #include "Logging.h" 16 #include "mozilla/PodOperations.h" 17 18 // #define DEBUG_DUMP_SURFACES 19 20 #ifdef DEBUG_DUMP_SURFACES 21 # include "gfxUtils.h" // not part of Moz2D 22 #endif 23 24 namespace mozilla { 25 namespace gfx { 26 27 namespace { 28 29 /** 30 * This class provides a way to get a pow() results in constant-time. It works 31 * by caching 257 ((1 << sCacheIndexPrecisionBits) + 1) values for bases between 32 * 0 and 1 and a fixed exponent. 33 **/ 34 class PowCache { 35 public: 36 PowCache() : mNumPowTablePreSquares(-1) {} 37 38 void CacheForExponent(Float aExponent) { 39 // Since we are in the world where we only care about 40 // input and results in [0,1], there is no point in 41 // dealing with non-positive exponents. 42 if (aExponent <= 0) { 43 mNumPowTablePreSquares = -1; 44 return; 45 } 46 int numPreSquares = 0; 47 while (numPreSquares < 5 && aExponent > (1 << (numPreSquares + 2))) { 48 numPreSquares++; 49 } 50 mNumPowTablePreSquares = numPreSquares; 51 for (size_t i = 0; i < sCacheSize; i++) { 52 // sCacheSize is chosen in such a way that a takes values 53 // from 0.0 to 1.0 inclusive. 54 Float a = i / Float(1 << sCacheIndexPrecisionBits); 55 MOZ_ASSERT(0.0f <= a && a <= 1.0f, 56 "We only want to cache for bases between 0 and 1."); 57 58 for (int j = 0; j < mNumPowTablePreSquares; j++) { 59 a = sqrt(a); 60 } 61 uint32_t cachedInt = pow(a, aExponent) * (1 << sOutputIntPrecisionBits); 62 MOZ_ASSERT(cachedInt < (1 << (sizeof(mPowTable[i]) * 8)), 63 "mPowCache integer type too small"); 64 65 mPowTable[i] = cachedInt; 66 } 67 } 68 69 // Only call Pow() if HasPowerTable() would return true, to avoid complicating 70 // this code and having it just return (1 << sOutputIntPrecisionBits)) 71 uint16_t Pow(uint16_t aBase) { 72 MOZ_ASSERT(HasPowerTable()); 73 // Results should be similar to what the following code would produce: 74 // Float x = Float(aBase) / (1 << sInputIntPrecisionBits); 75 // return uint16_t(pow(x, aExponent) * (1 << sOutputIntPrecisionBits)); 76 77 MOZ_ASSERT(aBase <= (1 << sInputIntPrecisionBits), 78 "aBase needs to be between 0 and 1!"); 79 80 uint32_t a = aBase; 81 for (int j = 0; j < mNumPowTablePreSquares; j++) { 82 a = a * a >> sInputIntPrecisionBits; 83 } 84 uint32_t i = a >> (sInputIntPrecisionBits - sCacheIndexPrecisionBits); 85 MOZ_ASSERT(i < sCacheSize, "out-of-bounds mPowTable access"); 86 return mPowTable[i]; 87 } 88 89 static const int sInputIntPrecisionBits = 15; 90 static const int sOutputIntPrecisionBits = 15; 91 static const int sCacheIndexPrecisionBits = 8; 92 93 inline bool HasPowerTable() const { return mNumPowTablePreSquares >= 0; } 94 95 private: 96 static const size_t sCacheSize = (1 << sCacheIndexPrecisionBits) + 1; 97 98 int mNumPowTablePreSquares; 99 uint16_t mPowTable[sCacheSize]; 100 }; 101 102 class PointLightSoftware { 103 public: 104 bool SetAttribute(uint32_t aIndex, Float) { return false; } 105 bool SetAttribute(uint32_t aIndex, const Point3D&); 106 void Prepare() {} 107 Point3D GetVectorToLight(const Point3D& aTargetPoint); 108 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight); 109 110 private: 111 Point3D mPosition; 112 }; 113 114 class SpotLightSoftware { 115 public: 116 SpotLightSoftware(); 117 bool SetAttribute(uint32_t aIndex, Float); 118 bool SetAttribute(uint32_t aIndex, const Point3D&); 119 void Prepare(); 120 Point3D GetVectorToLight(const Point3D& aTargetPoint); 121 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight); 122 123 private: 124 Point3D mPosition; 125 Point3D mPointsAt; 126 Point3D mVectorFromFocusPointToLight; 127 Float mSpecularFocus; 128 Float mLimitingConeAngle; 129 Float mLimitingConeCos; 130 PowCache mPowCache; 131 }; 132 133 class DistantLightSoftware { 134 public: 135 DistantLightSoftware(); 136 bool SetAttribute(uint32_t aIndex, Float); 137 bool SetAttribute(uint32_t aIndex, const Point3D&) { return false; } 138 void Prepare(); 139 Point3D GetVectorToLight(const Point3D& aTargetPoint); 140 uint32_t GetColor(uint32_t aLightColor, const Point3D& aVectorToLight); 141 142 private: 143 Float mAzimuth; 144 Float mElevation; 145 Point3D mVectorToLight; 146 }; 147 148 class DiffuseLightingSoftware { 149 public: 150 DiffuseLightingSoftware(); 151 bool SetAttribute(uint32_t aIndex, Float); 152 void Prepare() {} 153 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight, 154 uint32_t aColor); 155 156 private: 157 Float mDiffuseConstant; 158 }; 159 160 class SpecularLightingSoftware { 161 public: 162 SpecularLightingSoftware(); 163 bool SetAttribute(uint32_t aIndex, Float); 164 void Prepare(); 165 uint32_t LightPixel(const Point3D& aNormal, const Point3D& aVectorToLight, 166 uint32_t aColor); 167 168 private: 169 Float mSpecularConstant; 170 Float mSpecularExponent; 171 uint32_t mSpecularConstantInt; 172 PowCache mPowCache; 173 }; 174 175 } // unnamed namespace 176 177 // from xpcom/ds/nsMathUtils.h 178 static int32_t NS_lround(double x) { 179 return x >= 0.0 ? int32_t(x + 0.5) : int32_t(x - 0.5); 180 } 181 182 static already_AddRefed<DataSourceSurface> CloneAligned( 183 DataSourceSurface* aSource) { 184 return CreateDataSourceSurfaceByCloning(aSource); 185 } 186 187 static void FillRectWithPixel(DataSourceSurface* aSurface, 188 const IntRect& aFillRect, IntPoint aPixelPos) { 189 MOZ_ASSERT(!aFillRect.Overflows()); 190 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), 191 "aFillRect needs to be completely inside the surface"); 192 MOZ_ASSERT(SurfaceContainsPoint(aSurface, aPixelPos), 193 "aPixelPos needs to be inside the surface"); 194 195 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE); 196 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) { 197 return; 198 } 199 uint8_t* sourcePixelData = 200 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aPixelPos); 201 uint8_t* data = 202 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft()); 203 int bpp = BytesPerPixel(aSurface->GetFormat()); 204 205 // Fill the first row by hand. 206 if (bpp == 4) { 207 uint32_t sourcePixel = *(uint32_t*)sourcePixelData; 208 for (int32_t x = 0; x < aFillRect.Width(); x++) { 209 *((uint32_t*)data + x) = sourcePixel; 210 } 211 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { 212 uint8_t sourcePixel = *sourcePixelData; 213 memset(data, sourcePixel, aFillRect.Width()); 214 } 215 216 // Copy the first row into the other rows. 217 for (int32_t y = 1; y < aFillRect.Height(); y++) { 218 PodCopy(data + y * surfMap.GetStride(), data, aFillRect.Width() * bpp); 219 } 220 } 221 222 static void FillRectWithVerticallyRepeatingHorizontalStrip( 223 DataSourceSurface* aSurface, const IntRect& aFillRect, 224 const IntRect& aSampleRect) { 225 MOZ_ASSERT(!aFillRect.Overflows()); 226 MOZ_ASSERT(!aSampleRect.Overflows()); 227 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), 228 "aFillRect needs to be completely inside the surface"); 229 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), 230 "aSampleRect needs to be completely inside the surface"); 231 232 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE); 233 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) { 234 return; 235 } 236 237 uint8_t* sampleData = 238 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft()); 239 uint8_t* data = 240 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft()); 241 if (BytesPerPixel(aSurface->GetFormat()) == 4) { 242 for (int32_t y = 0; y < aFillRect.Height(); y++) { 243 PodCopy((uint32_t*)data, (uint32_t*)sampleData, aFillRect.Width()); 244 data += surfMap.GetStride(); 245 } 246 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { 247 for (int32_t y = 0; y < aFillRect.Height(); y++) { 248 PodCopy(data, sampleData, aFillRect.Width()); 249 data += surfMap.GetStride(); 250 } 251 } 252 } 253 254 static void FillRectWithHorizontallyRepeatingVerticalStrip( 255 DataSourceSurface* aSurface, const IntRect& aFillRect, 256 const IntRect& aSampleRect) { 257 MOZ_ASSERT(!aFillRect.Overflows()); 258 MOZ_ASSERT(!aSampleRect.Overflows()); 259 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFillRect), 260 "aFillRect needs to be completely inside the surface"); 261 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aSampleRect), 262 "aSampleRect needs to be completely inside the surface"); 263 264 DataSourceSurface::ScopedMap surfMap(aSurface, DataSourceSurface::READ_WRITE); 265 if (MOZ2D_WARN_IF(!surfMap.IsMapped())) { 266 return; 267 } 268 269 uint8_t* sampleData = 270 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aSampleRect.TopLeft()); 271 uint8_t* data = 272 DataAtOffset(aSurface, surfMap.GetMappedSurface(), aFillRect.TopLeft()); 273 if (BytesPerPixel(aSurface->GetFormat()) == 4) { 274 for (int32_t y = 0; y < aFillRect.Height(); y++) { 275 int32_t sampleColor = *((uint32_t*)sampleData); 276 for (int32_t x = 0; x < aFillRect.Width(); x++) { 277 *((uint32_t*)data + x) = sampleColor; 278 } 279 data += surfMap.GetStride(); 280 sampleData += surfMap.GetStride(); 281 } 282 } else if (BytesPerPixel(aSurface->GetFormat()) == 1) { 283 for (int32_t y = 0; y < aFillRect.Height(); y++) { 284 uint8_t sampleColor = *sampleData; 285 memset(data, sampleColor, aFillRect.Width()); 286 data += surfMap.GetStride(); 287 sampleData += surfMap.GetStride(); 288 } 289 } 290 } 291 292 static void DuplicateEdges(DataSourceSurface* aSurface, 293 const IntRect& aFromRect) { 294 MOZ_ASSERT(!aFromRect.Overflows()); 295 MOZ_ASSERT(IntRect(IntPoint(), aSurface->GetSize()).Contains(aFromRect), 296 "aFromRect needs to be completely inside the surface"); 297 298 IntSize size = aSurface->GetSize(); 299 IntRect fill; 300 IntRect sampleRect; 301 for (int32_t ix = 0; ix < 3; ix++) { 302 switch (ix) { 303 case 0: 304 fill.SetRectX(0, aFromRect.X()); 305 sampleRect.SetRectX(fill.XMost(), 1); 306 break; 307 case 1: 308 fill.SetRectX(aFromRect.X(), aFromRect.Width()); 309 sampleRect.SetRectX(fill.X(), fill.Width()); 310 break; 311 case 2: 312 fill.MoveToX(aFromRect.XMost()); 313 fill.SetRightEdge(size.width); 314 sampleRect.SetRectX(fill.X() - 1, 1); 315 break; 316 } 317 if (fill.Width() <= 0) { 318 continue; 319 } 320 bool xIsMiddle = (ix == 1); 321 for (int32_t iy = 0; iy < 3; iy++) { 322 switch (iy) { 323 case 0: 324 fill.SetRectY(0, aFromRect.Y()); 325 sampleRect.SetRectY(fill.YMost(), 1); 326 break; 327 case 1: 328 fill.SetRectY(aFromRect.Y(), aFromRect.Height()); 329 sampleRect.SetRectY(fill.Y(), fill.Height()); 330 break; 331 case 2: 332 fill.MoveToY(aFromRect.YMost()); 333 fill.SetBottomEdge(size.height); 334 sampleRect.SetRectY(fill.Y() - 1, 1); 335 break; 336 } 337 if (fill.Height() <= 0) { 338 continue; 339 } 340 bool yIsMiddle = (iy == 1); 341 if (!xIsMiddle && !yIsMiddle) { 342 // Corner 343 FillRectWithPixel(aSurface, fill, sampleRect.TopLeft()); 344 } 345 if (xIsMiddle && !yIsMiddle) { 346 // Top middle or bottom middle 347 FillRectWithVerticallyRepeatingHorizontalStrip(aSurface, fill, 348 sampleRect); 349 } 350 if (!xIsMiddle && yIsMiddle) { 351 // Left middle or right middle 352 FillRectWithHorizontallyRepeatingVerticalStrip(aSurface, fill, 353 sampleRect); 354 } 355 } 356 } 357 } 358 359 static IntPoint TileIndex(const IntRect& aFirstTileRect, 360 const IntPoint& aPoint) { 361 return IntPoint(int32_t(floor(double(aPoint.x - aFirstTileRect.X()) / 362 aFirstTileRect.Width())), 363 int32_t(floor(double(aPoint.y - aFirstTileRect.Y()) / 364 aFirstTileRect.Height()))); 365 } 366 367 static void TileSurface(DataSourceSurface* aSource, DataSourceSurface* aTarget, 368 const IntPoint& aOffset) { 369 IntRect sourceRect(aOffset, aSource->GetSize()); 370 IntRect targetRect(IntPoint(0, 0), aTarget->GetSize()); 371 IntPoint startIndex = TileIndex(sourceRect, targetRect.TopLeft()); 372 IntPoint endIndex = TileIndex(sourceRect, targetRect.BottomRight()); 373 374 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { 375 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { 376 IntPoint destPoint(sourceRect.X() + ix * sourceRect.Width(), 377 sourceRect.Y() + iy * sourceRect.Height()); 378 IntRect destRect(destPoint, sourceRect.Size()); 379 destRect = destRect.Intersect(targetRect); 380 IntRect srcRect = destRect - destPoint; 381 CopyRect(aSource, aTarget, srcRect, destRect.TopLeft()); 382 } 383 } 384 } 385 386 static already_AddRefed<DataSourceSurface> GetDataSurfaceInRect( 387 SourceSurface* aSurface, const IntRect& aSurfaceRect, 388 const IntRect& aDestRect, ConvolveMatrixEdgeMode aEdgeMode) { 389 MOZ_ASSERT(aSurface ? aSurfaceRect.Size() == aSurface->GetSize() 390 : aSurfaceRect.IsEmpty()); 391 392 if (aSurfaceRect.Overflows() || aDestRect.Overflows()) { 393 // We can't rely on the intersection calculations below to make sense when 394 // XMost() or YMost() overflow. Bail out. 395 return nullptr; 396 } 397 398 IntRect sourceRect = aSurfaceRect; 399 400 if (sourceRect.IsEqualEdges(aDestRect)) { 401 return aSurface ? aSurface->GetDataSurface() : nullptr; 402 } 403 404 IntRect intersect = sourceRect.Intersect(aDestRect); 405 406 // create rects that are in surface local space. 407 IntRect intersectInSourceSpace = intersect - sourceRect.TopLeft(); 408 IntRect intersectInDestSpace = intersect - aDestRect.TopLeft(); 409 SurfaceFormat format = 410 aSurface ? aSurface->GetFormat() : SurfaceFormat(SurfaceFormat::B8G8R8A8); 411 412 RefPtr<DataSourceSurface> target = 413 Factory::CreateDataSourceSurface(aDestRect.Size(), format, true); 414 if (MOZ2D_WARN_IF(!target)) { 415 return nullptr; 416 } 417 418 if (!aSurface) { 419 return target.forget(); 420 } 421 422 RefPtr<DataSourceSurface> dataSource = aSurface->GetDataSurface(); 423 MOZ_ASSERT(dataSource); 424 425 if (aEdgeMode == EDGE_MODE_WRAP) { 426 TileSurface(dataSource, target, intersectInDestSpace.TopLeft()); 427 return target.forget(); 428 } 429 430 CopyRect(dataSource, target, intersectInSourceSpace, 431 intersectInDestSpace.TopLeft()); 432 433 if (aEdgeMode == EDGE_MODE_DUPLICATE) { 434 DuplicateEdges(target, intersectInDestSpace); 435 } 436 437 return target.forget(); 438 } 439 440 /* static */ 441 already_AddRefed<FilterNode> FilterNodeSoftware::Create(FilterType aType) { 442 RefPtr<FilterNodeSoftware> filter; 443 switch (aType) { 444 case FilterType::BLEND: 445 filter = new FilterNodeBlendSoftware(); 446 break; 447 case FilterType::TRANSFORM: 448 filter = new FilterNodeTransformSoftware(); 449 break; 450 case FilterType::MORPHOLOGY: 451 filter = new FilterNodeMorphologySoftware(); 452 break; 453 case FilterType::COLOR_MATRIX: 454 filter = new FilterNodeColorMatrixSoftware(); 455 break; 456 case FilterType::FLOOD: 457 filter = new FilterNodeFloodSoftware(); 458 break; 459 case FilterType::TILE: 460 filter = new FilterNodeTileSoftware(); 461 break; 462 case FilterType::TABLE_TRANSFER: 463 filter = new FilterNodeTableTransferSoftware(); 464 break; 465 case FilterType::DISCRETE_TRANSFER: 466 filter = new FilterNodeDiscreteTransferSoftware(); 467 break; 468 case FilterType::LINEAR_TRANSFER: 469 filter = new FilterNodeLinearTransferSoftware(); 470 break; 471 case FilterType::GAMMA_TRANSFER: 472 filter = new FilterNodeGammaTransferSoftware(); 473 break; 474 case FilterType::CONVOLVE_MATRIX: 475 filter = new FilterNodeConvolveMatrixSoftware(); 476 break; 477 case FilterType::DISPLACEMENT_MAP: 478 filter = new FilterNodeDisplacementMapSoftware(); 479 break; 480 case FilterType::TURBULENCE: 481 filter = new FilterNodeTurbulenceSoftware(); 482 break; 483 case FilterType::ARITHMETIC_COMBINE: 484 filter = new FilterNodeArithmeticCombineSoftware(); 485 break; 486 case FilterType::COMPOSITE: 487 filter = new FilterNodeCompositeSoftware(); 488 break; 489 case FilterType::GAUSSIAN_BLUR: 490 filter = new FilterNodeGaussianBlurSoftware(); 491 break; 492 case FilterType::DIRECTIONAL_BLUR: 493 filter = new FilterNodeDirectionalBlurSoftware(); 494 break; 495 case FilterType::CROP: 496 filter = new FilterNodeCropSoftware(); 497 break; 498 case FilterType::PREMULTIPLY: 499 filter = new FilterNodePremultiplySoftware(); 500 break; 501 case FilterType::UNPREMULTIPLY: 502 filter = new FilterNodeUnpremultiplySoftware(); 503 break; 504 case FilterType::OPACITY: 505 filter = new FilterNodeOpacitySoftware(); 506 break; 507 case FilterType::POINT_DIFFUSE: 508 filter = new FilterNodeLightingSoftware<PointLightSoftware, 509 DiffuseLightingSoftware>( 510 "FilterNodeLightingSoftware<PointLight, DiffuseLighting>"); 511 break; 512 case FilterType::POINT_SPECULAR: 513 filter = new FilterNodeLightingSoftware<PointLightSoftware, 514 SpecularLightingSoftware>( 515 "FilterNodeLightingSoftware<PointLight, SpecularLighting>"); 516 break; 517 case FilterType::SPOT_DIFFUSE: 518 filter = new FilterNodeLightingSoftware<SpotLightSoftware, 519 DiffuseLightingSoftware>( 520 "FilterNodeLightingSoftware<SpotLight, DiffuseLighting>"); 521 break; 522 case FilterType::SPOT_SPECULAR: 523 filter = new FilterNodeLightingSoftware<SpotLightSoftware, 524 SpecularLightingSoftware>( 525 "FilterNodeLightingSoftware<SpotLight, SpecularLighting>"); 526 break; 527 case FilterType::DISTANT_DIFFUSE: 528 filter = new FilterNodeLightingSoftware<DistantLightSoftware, 529 DiffuseLightingSoftware>( 530 "FilterNodeLightingSoftware<DistantLight, DiffuseLighting>"); 531 break; 532 case FilterType::DISTANT_SPECULAR: 533 filter = new FilterNodeLightingSoftware<DistantLightSoftware, 534 SpecularLightingSoftware>( 535 "FilterNodeLightingSoftware<DistantLight, SpecularLighting>"); 536 break; 537 } 538 return filter.forget(); 539 } 540 541 void FilterNodeSoftware::Draw(DrawTarget* aDrawTarget, const Rect& aSourceRect, 542 const Point& aDestPoint, 543 const DrawOptions& aOptions) { 544 #ifdef DEBUG_DUMP_SURFACES 545 printf("<style>section{margin:10px;}</style><pre>\nRendering filter %s...\n", 546 GetName()); 547 #endif 548 549 Rect renderRect = aSourceRect; 550 renderRect.RoundOut(); 551 IntRect renderIntRect; 552 if (!renderRect.ToIntRect(&renderIntRect)) { 553 #ifdef DEBUG_DUMP_SURFACES 554 printf("render rect overflowed, not painting anything\n"); 555 printf("</pre>\n"); 556 #endif 557 return; 558 } 559 560 IntRect outputRect = GetOutputRectInRect(renderIntRect); 561 if (outputRect.Overflows()) { 562 #ifdef DEBUG_DUMP_SURFACES 563 printf("output rect overflowed, not painting anything\n"); 564 printf("</pre>\n"); 565 #endif 566 return; 567 } 568 569 RefPtr<DataSourceSurface> result; 570 if (!outputRect.IsEmpty()) { 571 result = GetOutput(outputRect); 572 } 573 574 if (!result) { 575 // Null results are allowed and treated as transparent. Don't draw anything. 576 #ifdef DEBUG_DUMP_SURFACES 577 printf("output returned null\n"); 578 printf("</pre>\n"); 579 #endif 580 return; 581 } 582 583 #ifdef DEBUG_DUMP_SURFACES 584 printf("output from %s:\n", GetName()); 585 printf("<img src='"); 586 gfxUtils::DumpAsDataURI(result); 587 printf("'>\n"); 588 printf("</pre>\n"); 589 #endif 590 591 Point sourceToDestOffset = aDestPoint - aSourceRect.TopLeft(); 592 Rect renderedSourceRect = Rect(outputRect).Intersect(aSourceRect); 593 Rect renderedDestRect = renderedSourceRect + sourceToDestOffset; 594 if (result->GetFormat() == SurfaceFormat::A8) { 595 // Interpret the result as having implicitly black color channels. 596 aDrawTarget->PushClipRect(renderedDestRect); 597 aDrawTarget->MaskSurface( 598 ColorPattern(DeviceColor::MaskOpaqueBlack()), result, 599 Point(outputRect.TopLeft()) + sourceToDestOffset, aOptions); 600 aDrawTarget->PopClip(); 601 } else { 602 aDrawTarget->DrawSurface(result, renderedDestRect, 603 renderedSourceRect - Point(outputRect.TopLeft()), 604 DrawSurfaceOptions(), aOptions); 605 } 606 } 607 608 already_AddRefed<DataSourceSurface> FilterNodeSoftware::GetOutput( 609 const IntRect& aRect) { 610 MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect)); 611 612 if (aRect.Overflows()) { 613 return nullptr; 614 } 615 616 IntRect cachedRect; 617 IntRect requestedRect; 618 RefPtr<DataSourceSurface> cachedOutput; 619 620 // Retrieve a cached surface if we have one and it can 621 // satisfy this request, or else request a rect we will compute and cache 622 if (!mCachedRect.Contains(aRect)) { 623 RequestRect(aRect); 624 requestedRect = mRequestedRect; 625 } else { 626 MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?"); 627 cachedRect = mCachedRect; 628 cachedOutput = mCachedOutput; 629 } 630 631 if (!cachedOutput) { 632 // Compute the output 633 cachedOutput = Render(requestedRect); 634 635 // Update the cache for future requests 636 mCachedOutput = cachedOutput; 637 if (!mCachedOutput) { 638 mCachedRect = IntRect(); 639 mRequestedRect = IntRect(); 640 return nullptr; 641 } 642 mCachedRect = requestedRect; 643 mRequestedRect = IntRect(); 644 645 cachedRect = mCachedRect; 646 } 647 648 return GetDataSurfaceInRect(cachedOutput, cachedRect, aRect, EDGE_MODE_NONE); 649 } 650 651 void FilterNodeSoftware::RequestRect(const IntRect& aRect) { 652 if (mRequestedRect.Contains(aRect)) { 653 // Bail out now. Otherwise pathological filters can spend time exponential 654 // in the number of primitives, e.g. if each primitive takes the 655 // previous primitive as its two inputs. 656 return; 657 } 658 mRequestedRect = mRequestedRect.Union(aRect); 659 RequestFromInputsForRect(aRect); 660 } 661 662 IntRect FilterNodeSoftware::MapInputRectToSource(uint32_t aInputEnumIndex, 663 const IntRect& aRect, 664 const IntRect& aMax, 665 FilterNode* aSourceNode) { 666 int32_t inputIndex = InputIndex(aInputEnumIndex); 667 if (inputIndex < 0) { 668 gfxDevCrash(LogReason::FilterInputError) 669 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs(); 670 return aMax; 671 } 672 if ((uint32_t)inputIndex < NumberOfSetInputs()) { 673 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; 674 // If we have any input filters call into them to do the mapping, 675 // otherwise we can assume an input surface will be used 676 // and just return aRect. 677 if (filter) { 678 return filter->MapRectToSource(aRect, aMax, aSourceNode); 679 } 680 } 681 // We have an input surface instead of a filter 682 // so check if we're the target node. 683 if (this == aSourceNode) { 684 return aRect; 685 } 686 return IntRect(); 687 } 688 689 void FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, 690 const IntRect& aRect) { 691 if (aRect.Overflows()) { 692 return; 693 } 694 695 int32_t inputIndex = InputIndex(aInputEnumIndex); 696 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { 697 gfxDevCrash(LogReason::FilterInputError) 698 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs(); 699 return; 700 } 701 if (mInputSurfaces[inputIndex]) { 702 return; 703 } 704 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; 705 MOZ_ASSERT(filter, "missing input"); 706 if (filter) { 707 filter->RequestRect(filter->GetOutputRectInRect(aRect)); 708 } 709 } 710 711 SurfaceFormat FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat, 712 FormatHint aFormatHint) { 713 if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) { 714 return SurfaceFormat::A8; 715 } 716 return SurfaceFormat::B8G8R8A8; 717 } 718 719 already_AddRefed<DataSourceSurface> 720 FilterNodeSoftware::GetInputDataSourceSurface( 721 uint32_t aInputEnumIndex, const IntRect& aRect, FormatHint aFormatHint, 722 ConvolveMatrixEdgeMode aEdgeMode, 723 const IntRect* aTransparencyPaddedSourceRect) { 724 if (aRect.Overflows()) { 725 return nullptr; 726 } 727 728 #ifdef DEBUG_DUMP_SURFACES 729 printf("<section><h1>GetInputDataSourceSurface with aRect: %s</h1>\n", 730 ToString(aRect).c_str()); 731 #endif 732 int32_t inputIndex = InputIndex(aInputEnumIndex); 733 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { 734 gfxDevCrash(LogReason::FilterInputData) 735 << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs(); 736 return nullptr; 737 } 738 739 if (aRect.IsEmpty()) { 740 return nullptr; 741 } 742 743 RefPtr<SourceSurface> surface; 744 IntRect surfaceRect; 745 746 if (mInputSurfaces[inputIndex]) { 747 // Input from input surface 748 surface = mInputSurfaces[inputIndex]; 749 #ifdef DEBUG_DUMP_SURFACES 750 printf("input from input surface:\n"); 751 #endif 752 surfaceRect = surface->GetRect(); 753 } else { 754 // Input from input filter 755 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; 756 MOZ_ASSERT(filter, "missing input"); 757 if (!filter) { 758 return nullptr; 759 } 760 #ifdef DEBUG_DUMP_SURFACES 761 printf("getting input from input filter %s...\n", filter->GetName()); 762 #endif 763 IntRect inputFilterOutput = filter->GetOutputRectInRect(aRect); 764 if (!inputFilterOutput.IsEmpty()) { 765 surface = filter->GetOutput(inputFilterOutput); 766 } 767 #ifdef DEBUG_DUMP_SURFACES 768 printf("input from input filter %s:\n", filter->GetName()); 769 #endif 770 surfaceRect = inputFilterOutput; 771 MOZ_ASSERT(!surface || surfaceRect.Size() == surface->GetSize()); 772 } 773 774 if (surface && surface->GetFormat() == SurfaceFormat::UNKNOWN) { 775 #ifdef DEBUG_DUMP_SURFACES 776 printf("wrong input format</section>\n\n"); 777 #endif 778 return nullptr; 779 } 780 781 if (!surfaceRect.IsEmpty() && !surface) { 782 #ifdef DEBUG_DUMP_SURFACES 783 printf(" -- no input --</section>\n\n"); 784 #endif 785 return nullptr; 786 } 787 788 if (aTransparencyPaddedSourceRect && 789 !aTransparencyPaddedSourceRect->IsEmpty()) { 790 IntRect srcRect = aTransparencyPaddedSourceRect->Intersect(aRect); 791 surface = 792 GetDataSurfaceInRect(surface, surfaceRect, srcRect, EDGE_MODE_NONE); 793 if (surface) { 794 surfaceRect = srcRect; 795 } else { 796 // Padding the surface with transparency failed, probably due to size 797 // restrictions. Since |surface| is now null, set the surfaceRect to 798 // empty so that we're consistent. 799 surfaceRect.SetEmpty(); 800 } 801 } 802 803 RefPtr<DataSourceSurface> result = 804 GetDataSurfaceInRect(surface, surfaceRect, aRect, aEdgeMode); 805 806 if (result) { 807 // TODO: This isn't safe since we don't have a guarantee 808 // that future Maps will have the same stride 809 DataSourceSurface::MappedSurface map; 810 if (result->Map(DataSourceSurface::READ, &map)) { 811 // Unmap immediately since CloneAligned hasn't been updated 812 // to use the Map API yet. We can still read the stride/data 813 // values as long as we don't try to dereference them. 814 result->Unmap(); 815 if (map.mStride != GetAlignedStride<16>(map.mStride, 1) || 816 reinterpret_cast<uintptr_t>(map.mData) % 16 != 0) { 817 // Align unaligned surface. 818 result = CloneAligned(result); 819 } 820 } else { 821 result = nullptr; 822 } 823 } 824 825 if (!result) { 826 #ifdef DEBUG_DUMP_SURFACES 827 printf(" -- no input --</section>\n\n"); 828 #endif 829 return nullptr; 830 } 831 832 SurfaceFormat currentFormat = result->GetFormat(); 833 if (DesiredFormat(currentFormat, aFormatHint) == SurfaceFormat::B8G8R8A8 && 834 currentFormat != SurfaceFormat::B8G8R8A8) { 835 result = FilterProcessing::ConvertToB8G8R8A8(result); 836 } 837 838 #ifdef DEBUG_DUMP_SURFACES 839 printf("<img src='"); 840 gfxUtils::DumpAsDataURI(result); 841 printf("'></section>"); 842 #endif 843 844 MOZ_ASSERT(!result || result->GetSize() == aRect.Size(), 845 "wrong surface size"); 846 847 return result.forget(); 848 } 849 850 IntRect FilterNodeSoftware::GetInputRectInRect(uint32_t aInputEnumIndex, 851 const IntRect& aInRect) { 852 if (aInRect.Overflows()) { 853 return IntRect(); 854 } 855 856 int32_t inputIndex = InputIndex(aInputEnumIndex); 857 if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) { 858 gfxDevCrash(LogReason::FilterInputRect) 859 << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs(); 860 return IntRect(); 861 } 862 if (mInputSurfaces[inputIndex]) { 863 return aInRect.Intersect(mInputSurfaces[inputIndex]->GetRect()); 864 } 865 RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex]; 866 MOZ_ASSERT(filter, "missing input"); 867 if (!filter) { 868 return IntRect(); 869 } 870 return filter->GetOutputRectInRect(aInRect); 871 } 872 873 size_t FilterNodeSoftware::NumberOfSetInputs() { 874 return std::max(mInputSurfaces.size(), mInputFilters.size()); 875 } 876 877 void FilterNodeSoftware::AddInvalidationListener( 878 FilterInvalidationListener* aListener) { 879 MOZ_ASSERT(aListener, "null listener"); 880 mInvalidationListeners.push_back(aListener); 881 } 882 883 void FilterNodeSoftware::RemoveInvalidationListener( 884 FilterInvalidationListener* aListener) { 885 MOZ_ASSERT(aListener, "null listener"); 886 std::vector<FilterInvalidationListener*>::iterator it = std::find( 887 mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener); 888 mInvalidationListeners.erase(it); 889 } 890 891 void FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter) { 892 Invalidate(); 893 } 894 895 void FilterNodeSoftware::Invalidate() { 896 mCachedOutput = nullptr; 897 mCachedRect = IntRect(); 898 for (std::vector<FilterInvalidationListener*>::iterator it = 899 mInvalidationListeners.begin(); 900 it != mInvalidationListeners.end(); it++) { 901 (*it)->FilterInvalidated(this); 902 } 903 } 904 905 FilterNodeSoftware::FilterNodeSoftware() {} 906 907 FilterNodeSoftware::~FilterNodeSoftware() { 908 MOZ_ASSERT( 909 mInvalidationListeners.empty(), 910 "All invalidation listeners should have unsubscribed themselves by now!"); 911 912 for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = 913 mInputFilters.begin(); 914 it != mInputFilters.end(); it++) { 915 if (*it) { 916 (*it)->RemoveInvalidationListener(this); 917 } 918 } 919 } 920 921 void FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode* aFilter) { 922 if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) { 923 MOZ_ASSERT(false, "can only take software filters as inputs"); 924 return; 925 } 926 SetInput(aIndex, nullptr, static_cast<FilterNodeSoftware*>(aFilter)); 927 } 928 929 void FilterNodeSoftware::SetInput(uint32_t aIndex, SourceSurface* aSurface) { 930 SetInput(aIndex, aSurface, nullptr); 931 } 932 933 void FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex, 934 SourceSurface* aSurface, 935 FilterNodeSoftware* aFilter) { 936 int32_t inputIndex = InputIndex(aInputEnumIndex); 937 if (inputIndex < 0) { 938 gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex; 939 return; 940 } 941 if ((uint32_t)inputIndex >= NumberOfSetInputs()) { 942 mInputSurfaces.resize(inputIndex + 1); 943 mInputFilters.resize(inputIndex + 1); 944 } 945 mInputSurfaces[inputIndex] = aSurface; 946 if (mInputFilters[inputIndex]) { 947 mInputFilters[inputIndex]->RemoveInvalidationListener(this); 948 } 949 if (aFilter) { 950 aFilter->AddInvalidationListener(this); 951 } 952 mInputFilters[inputIndex] = aFilter; 953 if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) { 954 mInputSurfaces.resize(inputIndex); 955 mInputFilters.resize(inputIndex); 956 } 957 Invalidate(); 958 } 959 960 FilterNodeBlendSoftware::FilterNodeBlendSoftware() 961 : mBlendMode(BLEND_MODE_MULTIPLY) {} 962 963 int32_t FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex) { 964 switch (aInputEnumIndex) { 965 case IN_BLEND_IN: 966 return 0; 967 case IN_BLEND_IN2: 968 return 1; 969 default: 970 return -1; 971 } 972 } 973 974 void FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, 975 uint32_t aBlendMode) { 976 MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE); 977 mBlendMode = static_cast<BlendMode>(aBlendMode); 978 Invalidate(); 979 } 980 981 static CompositionOp ToBlendOp(BlendMode aOp) { 982 switch (aOp) { 983 case BLEND_MODE_MULTIPLY: 984 return CompositionOp::OP_MULTIPLY; 985 case BLEND_MODE_SCREEN: 986 return CompositionOp::OP_SCREEN; 987 case BLEND_MODE_OVERLAY: 988 return CompositionOp::OP_OVERLAY; 989 case BLEND_MODE_DARKEN: 990 return CompositionOp::OP_DARKEN; 991 case BLEND_MODE_LIGHTEN: 992 return CompositionOp::OP_LIGHTEN; 993 case BLEND_MODE_COLOR_DODGE: 994 return CompositionOp::OP_COLOR_DODGE; 995 case BLEND_MODE_COLOR_BURN: 996 return CompositionOp::OP_COLOR_BURN; 997 case BLEND_MODE_HARD_LIGHT: 998 return CompositionOp::OP_HARD_LIGHT; 999 case BLEND_MODE_SOFT_LIGHT: 1000 return CompositionOp::OP_SOFT_LIGHT; 1001 case BLEND_MODE_DIFFERENCE: 1002 return CompositionOp::OP_DIFFERENCE; 1003 case BLEND_MODE_EXCLUSION: 1004 return CompositionOp::OP_EXCLUSION; 1005 case BLEND_MODE_HUE: 1006 return CompositionOp::OP_HUE; 1007 case BLEND_MODE_SATURATION: 1008 return CompositionOp::OP_SATURATION; 1009 case BLEND_MODE_COLOR: 1010 return CompositionOp::OP_COLOR; 1011 case BLEND_MODE_LUMINOSITY: 1012 return CompositionOp::OP_LUMINOSITY; 1013 } 1014 1015 MOZ_ASSERT_UNREACHABLE("Unexpected BlendMode"); 1016 return CompositionOp::OP_OVER; 1017 } 1018 1019 already_AddRefed<DataSourceSurface> FilterNodeBlendSoftware::Render( 1020 const IntRect& aRect) { 1021 RefPtr<DataSourceSurface> input1 = 1022 GetInputDataSourceSurface(IN_BLEND_IN, aRect, NEED_COLOR_CHANNELS); 1023 RefPtr<DataSourceSurface> input2 = 1024 GetInputDataSourceSurface(IN_BLEND_IN2, aRect, NEED_COLOR_CHANNELS); 1025 1026 // Null inputs need to be treated as transparent. 1027 1028 // First case: both are transparent. 1029 if (!input1 && !input2) { 1030 // Then the result is transparent, too. 1031 return nullptr; 1032 } 1033 1034 // Second case: one of them is transparent. Return the non-transparent one. 1035 if (!input1 || !input2) { 1036 return input1 ? input1.forget() : input2.forget(); 1037 } 1038 1039 // Third case: both are non-transparent. 1040 // Apply normal filtering. 1041 RefPtr<DataSourceSurface> target = 1042 FilterProcessing::ApplyBlending(input1, input2, mBlendMode); 1043 if (target != nullptr) { 1044 return target.forget(); 1045 } 1046 1047 IntSize size = input1->GetSize(); 1048 target = Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); 1049 if (MOZ2D_WARN_IF(!target)) { 1050 return nullptr; 1051 } 1052 1053 CopyRect(input1, target, IntRect(IntPoint(), size), IntPoint()); 1054 1055 // This needs to stay in scope until the draw target has been flushed. 1056 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE); 1057 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) { 1058 return nullptr; 1059 } 1060 1061 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData( 1062 BackendType::SKIA, targetMap.GetData(), target->GetSize(), 1063 targetMap.GetStride(), target->GetFormat()); 1064 1065 if (!dt) { 1066 gfxWarning() 1067 << "FilterNodeBlendSoftware::Render failed in CreateDrawTargetForData"; 1068 return nullptr; 1069 } 1070 1071 Rect r(0, 0, size.width, size.height); 1072 dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), 1073 DrawOptions(1.0f, ToBlendOp(mBlendMode))); 1074 dt->Flush(); 1075 return target.forget(); 1076 } 1077 1078 void FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect& aRect) { 1079 RequestInputRect(IN_BLEND_IN, aRect); 1080 RequestInputRect(IN_BLEND_IN2, aRect); 1081 } 1082 1083 IntRect FilterNodeBlendSoftware::MapRectToSource(const IntRect& aRect, 1084 const IntRect& aMax, 1085 FilterNode* aSourceNode) { 1086 IntRect result = MapInputRectToSource(IN_BLEND_IN, aRect, aMax, aSourceNode); 1087 result.OrWith(MapInputRectToSource(IN_BLEND_IN2, aRect, aMax, aSourceNode)); 1088 return result; 1089 } 1090 1091 IntRect FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect) { 1092 return GetInputRectInRect(IN_BLEND_IN, aRect) 1093 .Union(GetInputRectInRect(IN_BLEND_IN2, aRect)) 1094 .Intersect(aRect); 1095 } 1096 1097 FilterNodeTransformSoftware::FilterNodeTransformSoftware() 1098 : mSamplingFilter(SamplingFilter::GOOD) {} 1099 1100 int32_t FilterNodeTransformSoftware::InputIndex(uint32_t aInputEnumIndex) { 1101 switch (aInputEnumIndex) { 1102 case IN_TRANSFORM_IN: 1103 return 0; 1104 default: 1105 return -1; 1106 } 1107 } 1108 1109 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, 1110 uint32_t aFilter) { 1111 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); 1112 mSamplingFilter = static_cast<SamplingFilter>(aFilter); 1113 Invalidate(); 1114 } 1115 1116 void FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, 1117 const Matrix& aMatrix) { 1118 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); 1119 mMatrix = aMatrix; 1120 Invalidate(); 1121 } 1122 1123 IntRect FilterNodeTransformSoftware::SourceRectForOutputRect( 1124 const IntRect& aRect) { 1125 if (aRect.IsEmpty()) { 1126 return IntRect(); 1127 } 1128 1129 Matrix inverted(mMatrix); 1130 if (!inverted.Invert()) { 1131 return IntRect(); 1132 } 1133 1134 Rect neededRect = inverted.TransformBounds(Rect(aRect)); 1135 neededRect.RoundOut(); 1136 IntRect neededIntRect; 1137 if (!neededRect.ToIntRect(&neededIntRect)) { 1138 return IntRect(); 1139 } 1140 return GetInputRectInRect(IN_TRANSFORM_IN, neededIntRect); 1141 } 1142 1143 IntRect FilterNodeTransformSoftware::MapRectToSource(const IntRect& aRect, 1144 const IntRect& aMax, 1145 FilterNode* aSourceNode) { 1146 if (aRect.IsEmpty()) { 1147 return IntRect(); 1148 } 1149 1150 Matrix inverted(mMatrix); 1151 if (!inverted.Invert()) { 1152 return aMax; 1153 } 1154 1155 Rect neededRect = inverted.TransformBounds(Rect(aRect)); 1156 neededRect.RoundOut(); 1157 IntRect neededIntRect; 1158 if (!neededRect.ToIntRect(&neededIntRect)) { 1159 return aMax; 1160 } 1161 return MapInputRectToSource(IN_TRANSFORM_IN, neededIntRect, aMax, 1162 aSourceNode); 1163 } 1164 1165 already_AddRefed<DataSourceSurface> FilterNodeTransformSoftware::Render( 1166 const IntRect& aRect) { 1167 IntRect srcRect = SourceRectForOutputRect(aRect); 1168 1169 RefPtr<DataSourceSurface> input = 1170 GetInputDataSourceSurface(IN_TRANSFORM_IN, srcRect); 1171 1172 if (!input) { 1173 return nullptr; 1174 } 1175 1176 Matrix transform = Matrix::Translation(srcRect.X(), srcRect.Y()) * mMatrix * 1177 Matrix::Translation(-aRect.X(), -aRect.Y()); 1178 if (transform.IsIdentity() && srcRect.Size() == aRect.Size()) { 1179 return input.forget(); 1180 } 1181 1182 RefPtr<DataSourceSurface> surf = 1183 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat(), true); 1184 1185 if (!surf) { 1186 return nullptr; 1187 } 1188 1189 DataSourceSurface::MappedSurface mapping; 1190 if (!surf->Map(DataSourceSurface::MapType::WRITE, &mapping)) { 1191 gfxCriticalError() 1192 << "FilterNodeTransformSoftware::Render failed to map surface"; 1193 return nullptr; 1194 } 1195 1196 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData( 1197 BackendType::SKIA, mapping.mData, surf->GetSize(), mapping.mStride, 1198 surf->GetFormat()); 1199 if (!dt) { 1200 gfxWarning() << "FilterNodeTransformSoftware::Render failed in " 1201 "CreateDrawTargetForData"; 1202 return nullptr; 1203 } 1204 1205 Rect r(0, 0, srcRect.Width(), srcRect.Height()); 1206 dt->SetTransform(transform); 1207 dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter)); 1208 1209 dt->Flush(); 1210 surf->Unmap(); 1211 return surf.forget(); 1212 } 1213 1214 void FilterNodeTransformSoftware::RequestFromInputsForRect( 1215 const IntRect& aRect) { 1216 RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect)); 1217 } 1218 1219 IntRect FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect) { 1220 IntRect srcRect = SourceRectForOutputRect(aRect); 1221 if (srcRect.IsEmpty()) { 1222 return IntRect(); 1223 } 1224 1225 Rect outRect = mMatrix.TransformBounds(Rect(srcRect)); 1226 outRect.RoundOut(); 1227 IntRect outIntRect; 1228 if (!outRect.ToIntRect(&outIntRect)) { 1229 return IntRect(); 1230 } 1231 return outIntRect.Intersect(aRect); 1232 } 1233 1234 FilterNodeMorphologySoftware::FilterNodeMorphologySoftware() 1235 : mOperator(MORPHOLOGY_OPERATOR_ERODE) {} 1236 1237 int32_t FilterNodeMorphologySoftware::InputIndex(uint32_t aInputEnumIndex) { 1238 switch (aInputEnumIndex) { 1239 case IN_MORPHOLOGY_IN: 1240 return 0; 1241 default: 1242 return -1; 1243 } 1244 } 1245 1246 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, 1247 const IntSize& aRadii) { 1248 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII); 1249 mRadii.width = std::clamp(aRadii.width, 0, 100000); 1250 mRadii.height = std::clamp(aRadii.height, 0, 100000); 1251 Invalidate(); 1252 } 1253 1254 void FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex, 1255 uint32_t aOperator) { 1256 MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR); 1257 mOperator = static_cast<MorphologyOperator>(aOperator); 1258 Invalidate(); 1259 } 1260 1261 static already_AddRefed<DataSourceSurface> ApplyMorphology( 1262 const IntRect& aSourceRect, DataSourceSurface* aInput, 1263 const IntRect& aDestRect, int32_t rx, int32_t ry, 1264 MorphologyOperator aOperator) { 1265 IntRect srcRect = aSourceRect - aDestRect.TopLeft(); 1266 IntRect destRect = aDestRect - aDestRect.TopLeft(); 1267 IntRect tmpRect(destRect.X(), srcRect.Y(), destRect.Width(), 1268 srcRect.Height()); 1269 #ifdef DEBUG 1270 IntMargin margin = srcRect - destRect; 1271 MOZ_ASSERT(margin.top >= ry && margin.right >= rx && margin.bottom >= ry && 1272 margin.left >= rx, 1273 "insufficient margin"); 1274 #endif 1275 1276 RefPtr<DataSourceSurface> tmp; 1277 if (rx == 0) { 1278 tmp = aInput; 1279 } else { 1280 tmp = Factory::CreateDataSourceSurface(tmpRect.Size(), 1281 SurfaceFormat::B8G8R8A8); 1282 if (MOZ2D_WARN_IF(!tmp)) { 1283 return nullptr; 1284 } 1285 1286 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ); 1287 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::WRITE); 1288 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !tmpMap.IsMapped())) { 1289 return nullptr; 1290 } 1291 const uint8_t* sourceData = 1292 DataAtOffset(aInput, sourceMap.GetMappedSurface(), 1293 destRect.TopLeft() - srcRect.TopLeft()); 1294 uint8_t* tmpData = DataAtOffset(tmp, tmpMap.GetMappedSurface(), 1295 destRect.TopLeft() - tmpRect.TopLeft()); 1296 1297 FilterProcessing::ApplyMorphologyHorizontal( 1298 sourceData, sourceMap.GetStride(), tmpData, tmpMap.GetStride(), tmpRect, 1299 rx, aOperator); 1300 } 1301 1302 RefPtr<DataSourceSurface> dest; 1303 if (ry == 0) { 1304 dest = tmp; 1305 } else { 1306 dest = Factory::CreateDataSourceSurface(destRect.Size(), 1307 SurfaceFormat::B8G8R8A8); 1308 if (MOZ2D_WARN_IF(!dest)) { 1309 return nullptr; 1310 } 1311 1312 DataSourceSurface::ScopedMap tmpMap(tmp, DataSourceSurface::READ); 1313 DataSourceSurface::ScopedMap destMap(dest, DataSourceSurface::WRITE); 1314 if (MOZ2D_WARN_IF(!tmpMap.IsMapped() || !destMap.IsMapped())) { 1315 return nullptr; 1316 } 1317 int32_t tmpStride = tmpMap.GetStride(); 1318 const uint8_t* tmpData = DataAtOffset( 1319 tmp, tmpMap.GetMappedSurface(), destRect.TopLeft() - tmpRect.TopLeft()); 1320 1321 int32_t destStride = destMap.GetStride(); 1322 uint8_t* destData = destMap.GetData(); 1323 1324 FilterProcessing::ApplyMorphologyVertical( 1325 tmpData, tmpStride, destData, destStride, destRect, ry, aOperator); 1326 } 1327 1328 return dest.forget(); 1329 } 1330 1331 already_AddRefed<DataSourceSurface> FilterNodeMorphologySoftware::Render( 1332 const IntRect& aRect) { 1333 IntRect srcRect = aRect; 1334 srcRect.Inflate(mRadii); 1335 1336 RefPtr<DataSourceSurface> input = 1337 GetInputDataSourceSurface(IN_MORPHOLOGY_IN, srcRect, NEED_COLOR_CHANNELS); 1338 if (!input) { 1339 return nullptr; 1340 } 1341 1342 int32_t rx = mRadii.width; 1343 int32_t ry = mRadii.height; 1344 1345 if (rx == 0 && ry == 0) { 1346 return input.forget(); 1347 } 1348 1349 return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator); 1350 } 1351 1352 void FilterNodeMorphologySoftware::RequestFromInputsForRect( 1353 const IntRect& aRect) { 1354 IntRect srcRect = aRect; 1355 srcRect.Inflate(mRadii); 1356 RequestInputRect(IN_MORPHOLOGY_IN, srcRect); 1357 } 1358 1359 IntRect FilterNodeMorphologySoftware::GetOutputRectInRect( 1360 const IntRect& aRect) { 1361 IntRect inflatedSourceRect = aRect; 1362 inflatedSourceRect.Inflate(mRadii); 1363 IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect); 1364 if (mOperator == MORPHOLOGY_OPERATOR_ERODE) { 1365 inputRect.Deflate(mRadii); 1366 } else { 1367 inputRect.Inflate(mRadii); 1368 } 1369 return inputRect.Intersect(aRect); 1370 } 1371 1372 int32_t FilterNodeColorMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) { 1373 switch (aInputEnumIndex) { 1374 case IN_COLOR_MATRIX_IN: 1375 return 0; 1376 default: 1377 return -1; 1378 } 1379 } 1380 1381 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, 1382 const Matrix5x4& aMatrix) { 1383 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); 1384 mMatrix = aMatrix; 1385 Invalidate(); 1386 } 1387 1388 void FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex, 1389 uint32_t aAlphaMode) { 1390 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); 1391 mAlphaMode = (AlphaMode)aAlphaMode; 1392 Invalidate(); 1393 } 1394 1395 static already_AddRefed<DataSourceSurface> Premultiply( 1396 DataSourceSurface* aSurface) { 1397 if (aSurface->GetFormat() == SurfaceFormat::A8) { 1398 RefPtr<DataSourceSurface> surface(aSurface); 1399 return surface.forget(); 1400 } 1401 1402 IntSize size = aSurface->GetSize(); 1403 RefPtr<DataSourceSurface> target = 1404 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); 1405 if (MOZ2D_WARN_IF(!target)) { 1406 return nullptr; 1407 } 1408 1409 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ); 1410 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 1411 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) { 1412 return nullptr; 1413 } 1414 1415 const uint8_t* inputData = inputMap.GetData(); 1416 int32_t inputStride = inputMap.GetStride(); 1417 uint8_t* targetData = targetMap.GetData(); 1418 int32_t targetStride = targetMap.GetStride(); 1419 1420 FilterProcessing::DoPremultiplicationCalculation( 1421 size, targetData, targetStride, inputData, inputStride); 1422 1423 return target.forget(); 1424 } 1425 1426 static already_AddRefed<DataSourceSurface> Unpremultiply( 1427 DataSourceSurface* aSurface) { 1428 if (aSurface->GetFormat() == SurfaceFormat::A8) { 1429 RefPtr<DataSourceSurface> surface(aSurface); 1430 return surface.forget(); 1431 } 1432 1433 IntSize size = aSurface->GetSize(); 1434 RefPtr<DataSourceSurface> target = 1435 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); 1436 if (MOZ2D_WARN_IF(!target)) { 1437 return nullptr; 1438 } 1439 1440 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ); 1441 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 1442 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) { 1443 return nullptr; 1444 } 1445 1446 uint8_t* inputData = inputMap.GetData(); 1447 int32_t inputStride = inputMap.GetStride(); 1448 uint8_t* targetData = targetMap.GetData(); 1449 int32_t targetStride = targetMap.GetStride(); 1450 1451 FilterProcessing::DoUnpremultiplicationCalculation( 1452 size, targetData, targetStride, inputData, inputStride); 1453 1454 return target.forget(); 1455 } 1456 1457 static already_AddRefed<DataSourceSurface> Opacity(DataSourceSurface* aSurface, 1458 Float aValue) { 1459 if (aValue == 1.0f) { 1460 RefPtr<DataSourceSurface> surface(aSurface); 1461 return surface.forget(); 1462 } 1463 1464 IntSize size = aSurface->GetSize(); 1465 RefPtr<DataSourceSurface> target = 1466 Factory::CreateDataSourceSurface(size, aSurface->GetFormat()); 1467 if (MOZ2D_WARN_IF(!target)) { 1468 return nullptr; 1469 } 1470 1471 DataSourceSurface::ScopedMap inputMap(aSurface, DataSourceSurface::READ); 1472 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 1473 if (MOZ2D_WARN_IF(!inputMap.IsMapped() || !targetMap.IsMapped())) { 1474 return nullptr; 1475 } 1476 1477 uint8_t* inputData = inputMap.GetData(); 1478 int32_t inputStride = inputMap.GetStride(); 1479 uint8_t* targetData = targetMap.GetData(); 1480 int32_t targetStride = targetMap.GetStride(); 1481 1482 if (aSurface->GetFormat() == SurfaceFormat::A8) { 1483 FilterProcessing::DoOpacityCalculationA8(size, targetData, targetStride, 1484 inputData, inputStride, aValue); 1485 } else { 1486 MOZ_ASSERT(aSurface->GetFormat() == SurfaceFormat::B8G8R8A8); 1487 FilterProcessing::DoOpacityCalculation(size, targetData, targetStride, 1488 inputData, inputStride, aValue); 1489 } 1490 1491 return target.forget(); 1492 } 1493 1494 already_AddRefed<DataSourceSurface> FilterNodeColorMatrixSoftware::Render( 1495 const IntRect& aRect) { 1496 RefPtr<DataSourceSurface> input = 1497 GetInputDataSourceSurface(IN_COLOR_MATRIX_IN, aRect, NEED_COLOR_CHANNELS); 1498 if (!input) { 1499 return nullptr; 1500 } 1501 1502 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { 1503 input = Unpremultiply(input); 1504 } 1505 1506 RefPtr<DataSourceSurface> result = 1507 FilterProcessing::ApplyColorMatrix(input, mMatrix); 1508 1509 if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) { 1510 result = Premultiply(result); 1511 } 1512 1513 return result.forget(); 1514 } 1515 1516 void FilterNodeColorMatrixSoftware::RequestFromInputsForRect( 1517 const IntRect& aRect) { 1518 RequestInputRect(IN_COLOR_MATRIX_IN, aRect); 1519 } 1520 1521 IntRect FilterNodeColorMatrixSoftware::MapRectToSource( 1522 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 1523 return MapInputRectToSource(IN_COLOR_MATRIX_IN, aRect, aMax, aSourceNode); 1524 } 1525 1526 IntRect FilterNodeColorMatrixSoftware::GetOutputRectInRect( 1527 const IntRect& aRect) { 1528 if (mMatrix._54 > 0.0f) { 1529 return aRect; 1530 } 1531 return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect); 1532 } 1533 1534 void FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, 1535 const DeviceColor& aColor) { 1536 MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR); 1537 mColor = aColor; 1538 Invalidate(); 1539 } 1540 1541 static uint32_t ColorToBGRA(const DeviceColor& aColor) { 1542 union { 1543 uint32_t color; 1544 uint8_t components[4]; 1545 }; 1546 components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = 1547 NS_lround(aColor.r * aColor.a * 255.0f); 1548 components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = 1549 NS_lround(aColor.g * aColor.a * 255.0f); 1550 components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = 1551 NS_lround(aColor.b * aColor.a * 255.0f); 1552 components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = NS_lround(aColor.a * 255.0f); 1553 return color; 1554 } 1555 1556 static SurfaceFormat FormatForColor(DeviceColor aColor) { 1557 if (aColor.r == 0 && aColor.g == 0 && aColor.b == 0) { 1558 return SurfaceFormat::A8; 1559 } 1560 return SurfaceFormat::B8G8R8A8; 1561 } 1562 1563 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::Render( 1564 const IntRect& aRect) { 1565 SurfaceFormat format = FormatForColor(mColor); 1566 RefPtr<DataSourceSurface> target = 1567 Factory::CreateDataSourceSurface(aRect.Size(), format); 1568 if (MOZ2D_WARN_IF(!target)) { 1569 return nullptr; 1570 } 1571 1572 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 1573 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) { 1574 return nullptr; 1575 } 1576 1577 uint8_t* targetData = targetMap.GetData(); 1578 int32_t stride = targetMap.GetStride(); 1579 1580 if (format == SurfaceFormat::B8G8R8A8) { 1581 uint32_t color = ColorToBGRA(mColor); 1582 for (int32_t y = 0; y < aRect.Height(); y++) { 1583 for (int32_t x = 0; x < aRect.Width(); x++) { 1584 *((uint32_t*)targetData + x) = color; 1585 } 1586 PodZero(&targetData[aRect.Width() * 4], stride - aRect.Width() * 4); 1587 targetData += stride; 1588 } 1589 } else if (format == SurfaceFormat::A8) { 1590 uint8_t alpha = NS_lround(mColor.a * 255.0f); 1591 for (int32_t y = 0; y < aRect.Height(); y++) { 1592 for (int32_t x = 0; x < aRect.Width(); x++) { 1593 targetData[x] = alpha; 1594 } 1595 PodZero(&targetData[aRect.Width()], stride - aRect.Width()); 1596 targetData += stride; 1597 } 1598 } else { 1599 gfxDevCrash(LogReason::FilterInputFormat) 1600 << "Bad format in flood render " << (int)format; 1601 return nullptr; 1602 } 1603 1604 return target.forget(); 1605 } 1606 1607 // Override GetOutput to get around caching. Rendering simple floods is 1608 // comparatively fast. 1609 already_AddRefed<DataSourceSurface> FilterNodeFloodSoftware::GetOutput( 1610 const IntRect& aRect) { 1611 return Render(aRect); 1612 } 1613 1614 IntRect FilterNodeFloodSoftware::MapRectToSource(const IntRect& aRect, 1615 const IntRect& aMax, 1616 FilterNode* aSourceNode) { 1617 return IntRect(); 1618 } 1619 1620 IntRect FilterNodeFloodSoftware::GetOutputRectInRect(const IntRect& aRect) { 1621 if (mColor.a == 0.0f) { 1622 return IntRect(); 1623 } 1624 return aRect; 1625 } 1626 1627 int32_t FilterNodeTileSoftware::InputIndex(uint32_t aInputEnumIndex) { 1628 switch (aInputEnumIndex) { 1629 case IN_TILE_IN: 1630 return 0; 1631 default: 1632 return -1; 1633 } 1634 } 1635 1636 void FilterNodeTileSoftware::SetAttribute(uint32_t aIndex, 1637 const IntRect& aSourceRect) { 1638 MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT); 1639 mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()), 1640 int32_t(aSourceRect.Width()), 1641 int32_t(aSourceRect.Height())); 1642 Invalidate(); 1643 } 1644 1645 namespace { 1646 struct CompareIntRects { 1647 bool operator()(const IntRect& a, const IntRect& b) const { 1648 if (a.X() != b.X()) { 1649 return a.X() < b.X(); 1650 } 1651 if (a.Y() != b.Y()) { 1652 return a.Y() < b.Y(); 1653 } 1654 if (a.Width() != b.Width()) { 1655 return a.Width() < b.Width(); 1656 } 1657 return a.Height() < b.Height(); 1658 } 1659 }; 1660 1661 } // namespace 1662 1663 already_AddRefed<DataSourceSurface> FilterNodeTileSoftware::Render( 1664 const IntRect& aRect) { 1665 if (mSourceRect.IsEmpty()) { 1666 return nullptr; 1667 } 1668 1669 if (mSourceRect.Contains(aRect)) { 1670 return GetInputDataSourceSurface(IN_TILE_IN, aRect); 1671 } 1672 1673 RefPtr<DataSourceSurface> target; 1674 1675 typedef std::map<IntRect, RefPtr<DataSourceSurface>, CompareIntRects> 1676 InputMap; 1677 InputMap inputs; 1678 1679 IntPoint startIndex = TileIndex(mSourceRect, aRect.TopLeft()); 1680 IntPoint endIndex = TileIndex(mSourceRect, aRect.BottomRight()); 1681 for (int32_t ix = startIndex.x; ix <= endIndex.x; ix++) { 1682 for (int32_t iy = startIndex.y; iy <= endIndex.y; iy++) { 1683 IntPoint sourceToDestOffset(ix * mSourceRect.Width(), 1684 iy * mSourceRect.Height()); 1685 IntRect destRect = aRect.Intersect(mSourceRect + sourceToDestOffset); 1686 IntRect srcRect = destRect - sourceToDestOffset; 1687 if (srcRect.IsEmpty()) { 1688 continue; 1689 } 1690 1691 RefPtr<DataSourceSurface> input; 1692 InputMap::iterator it = inputs.find(srcRect); 1693 if (it == inputs.end()) { 1694 input = GetInputDataSourceSurface(IN_TILE_IN, srcRect); 1695 inputs[srcRect] = input; 1696 } else { 1697 input = it->second; 1698 } 1699 if (!input) { 1700 return nullptr; 1701 } 1702 if (!target) { 1703 // We delay creating the target until now because we want to use the 1704 // same format as our input filter, and we do not actually know the 1705 // input format before we call GetInputDataSourceSurface. 1706 target = 1707 Factory::CreateDataSourceSurface(aRect.Size(), input->GetFormat()); 1708 if (MOZ2D_WARN_IF(!target)) { 1709 return nullptr; 1710 } 1711 } 1712 1713 if (input->GetFormat() != target->GetFormat()) { 1714 // Different rectangles of the input can have different formats. If 1715 // that happens, just convert everything to B8G8R8A8. 1716 target = FilterProcessing::ConvertToB8G8R8A8(target); 1717 input = FilterProcessing::ConvertToB8G8R8A8(input); 1718 if (MOZ2D_WARN_IF(!target) || MOZ2D_WARN_IF(!input)) { 1719 return nullptr; 1720 } 1721 } 1722 1723 CopyRect(input, target, srcRect - srcRect.TopLeft(), 1724 destRect.TopLeft() - aRect.TopLeft()); 1725 } 1726 } 1727 1728 return target.forget(); 1729 } 1730 1731 void FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect& aRect) { 1732 // Do not request anything. 1733 // Source rects for the tile filter can be discontinuous with large gaps 1734 // between them. Requesting those from our input filter might cause it to 1735 // render the whole bounding box of all of them, which would be wasteful. 1736 } 1737 1738 IntRect FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect) { 1739 return aRect; 1740 } 1741 1742 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware() 1743 : mDisableR(true), mDisableG(true), mDisableB(true), mDisableA(true) {} 1744 1745 void FilterNodeComponentTransferSoftware::SetAttribute(uint32_t aIndex, 1746 bool aDisable) { 1747 switch (aIndex) { 1748 case ATT_TRANSFER_DISABLE_R: 1749 mDisableR = aDisable; 1750 break; 1751 case ATT_TRANSFER_DISABLE_G: 1752 mDisableG = aDisable; 1753 break; 1754 case ATT_TRANSFER_DISABLE_B: 1755 mDisableB = aDisable; 1756 break; 1757 case ATT_TRANSFER_DISABLE_A: 1758 mDisableA = aDisable; 1759 break; 1760 default: 1761 MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute"); 1762 } 1763 Invalidate(); 1764 } 1765 1766 void FilterNodeComponentTransferSoftware::GenerateLookupTable( 1767 ptrdiff_t aComponent, uint8_t aTables[4][256], bool aDisabled) { 1768 if (aDisabled) { 1769 for (int32_t i = 0; i < 256; ++i) { 1770 aTables[aComponent][i] = i; 1771 } 1772 } else { 1773 FillLookupTable(aComponent, aTables[aComponent]); 1774 } 1775 } 1776 1777 template <uint32_t BytesPerPixel> 1778 static void TransferComponents( 1779 DataSourceSurface* aInput, DataSourceSurface* aTarget, 1780 const uint8_t aLookupTables[BytesPerPixel][256]) { 1781 MOZ_ASSERT(aInput->GetFormat() == aTarget->GetFormat(), "different formats"); 1782 IntSize size = aInput->GetSize(); 1783 1784 DataSourceSurface::ScopedMap sourceMap(aInput, DataSourceSurface::READ); 1785 DataSourceSurface::ScopedMap targetMap(aTarget, DataSourceSurface::WRITE); 1786 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) { 1787 return; 1788 } 1789 1790 uint8_t* sourceData = sourceMap.GetData(); 1791 int32_t sourceStride = sourceMap.GetStride(); 1792 uint8_t* targetData = targetMap.GetData(); 1793 int32_t targetStride = targetMap.GetStride(); 1794 1795 MOZ_ASSERT(sourceStride <= targetStride, "target smaller than source"); 1796 1797 for (int32_t y = 0; y < size.height; y++) { 1798 for (int32_t x = 0; x < size.width; x++) { 1799 uint32_t sourceIndex = y * sourceStride + x * BytesPerPixel; 1800 uint32_t targetIndex = y * targetStride + x * BytesPerPixel; 1801 for (uint32_t i = 0; i < BytesPerPixel; i++) { 1802 targetData[targetIndex + i] = 1803 aLookupTables[i][sourceData[sourceIndex + i]]; 1804 } 1805 } 1806 1807 // Zero padding to keep valgrind happy. 1808 PodZero(&targetData[y * targetStride + size.width * BytesPerPixel], 1809 targetStride - size.width * BytesPerPixel); 1810 } 1811 } 1812 1813 static bool IsAllZero(const uint8_t aLookupTable[256]) { 1814 for (int32_t i = 0; i < 256; i++) { 1815 if (aLookupTable[i] != 0) { 1816 return false; 1817 } 1818 } 1819 return true; 1820 } 1821 1822 already_AddRefed<DataSourceSurface> FilterNodeComponentTransferSoftware::Render( 1823 const IntRect& aRect) { 1824 if (mDisableR && mDisableG && mDisableB && mDisableA) { 1825 return GetInputDataSourceSurface(IN_TRANSFER_IN, aRect); 1826 } 1827 1828 uint8_t lookupTables[4][256]; 1829 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_R, lookupTables, mDisableR); 1830 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_G, lookupTables, mDisableG); 1831 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_B, lookupTables, mDisableB); 1832 GenerateLookupTable(B8G8R8A8_COMPONENT_BYTEOFFSET_A, lookupTables, mDisableA); 1833 1834 bool needColorChannels = 1835 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R][0] != 0 || 1836 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G][0] != 0 || 1837 lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B][0] != 0; 1838 1839 FormatHint pref = needColorChannels ? NEED_COLOR_CHANNELS : CAN_HANDLE_A8; 1840 1841 RefPtr<DataSourceSurface> input = 1842 GetInputDataSourceSurface(IN_TRANSFER_IN, aRect, pref); 1843 if (!input) { 1844 return nullptr; 1845 } 1846 1847 if (input->GetFormat() == SurfaceFormat::B8G8R8A8 && !needColorChannels) { 1848 bool colorChannelsBecomeBlack = 1849 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) && 1850 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) && 1851 IsAllZero(lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_B]); 1852 1853 if (colorChannelsBecomeBlack) { 1854 input = FilterProcessing::ExtractAlpha(input); 1855 } 1856 } 1857 1858 SurfaceFormat format = input->GetFormat(); 1859 if (format == SurfaceFormat::A8 && mDisableA) { 1860 return input.forget(); 1861 } 1862 1863 RefPtr<DataSourceSurface> target = 1864 Factory::CreateDataSourceSurface(aRect.Size(), format); 1865 if (MOZ2D_WARN_IF(!target)) { 1866 return nullptr; 1867 } 1868 1869 if (format == SurfaceFormat::A8) { 1870 TransferComponents<1>(input, target, 1871 &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]); 1872 } else { 1873 TransferComponents<4>(input, target, lookupTables); 1874 } 1875 1876 return target.forget(); 1877 } 1878 1879 void FilterNodeComponentTransferSoftware::RequestFromInputsForRect( 1880 const IntRect& aRect) { 1881 RequestInputRect(IN_TRANSFER_IN, aRect); 1882 } 1883 1884 IntRect FilterNodeComponentTransferSoftware::MapRectToSource( 1885 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 1886 return MapInputRectToSource(IN_TRANSFER_IN, aRect, aMax, aSourceNode); 1887 } 1888 1889 IntRect FilterNodeComponentTransferSoftware::GetOutputRectInRect( 1890 const IntRect& aRect) { 1891 if (mDisableA) { 1892 return GetInputRectInRect(IN_TRANSFER_IN, aRect); 1893 } 1894 return aRect; 1895 } 1896 1897 int32_t FilterNodeComponentTransferSoftware::InputIndex( 1898 uint32_t aInputEnumIndex) { 1899 switch (aInputEnumIndex) { 1900 case IN_TRANSFER_IN: 1901 return 0; 1902 default: 1903 return -1; 1904 } 1905 } 1906 1907 void FilterNodeTableTransferSoftware::SetAttribute(uint32_t aIndex, 1908 const Float* aFloat, 1909 uint32_t aSize) { 1910 std::vector<Float> table(aFloat, aFloat + aSize); 1911 switch (aIndex) { 1912 case ATT_TABLE_TRANSFER_TABLE_R: 1913 mTableR = table; 1914 break; 1915 case ATT_TABLE_TRANSFER_TABLE_G: 1916 mTableG = table; 1917 break; 1918 case ATT_TABLE_TRANSFER_TABLE_B: 1919 mTableB = table; 1920 break; 1921 case ATT_TABLE_TRANSFER_TABLE_A: 1922 mTableA = table; 1923 break; 1924 default: 1925 MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute"); 1926 } 1927 Invalidate(); 1928 } 1929 1930 void FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent, 1931 uint8_t aTable[256]) { 1932 switch (aComponent) { 1933 case B8G8R8A8_COMPONENT_BYTEOFFSET_R: 1934 FillLookupTableImpl(mTableR, aTable); 1935 break; 1936 case B8G8R8A8_COMPONENT_BYTEOFFSET_G: 1937 FillLookupTableImpl(mTableG, aTable); 1938 break; 1939 case B8G8R8A8_COMPONENT_BYTEOFFSET_B: 1940 FillLookupTableImpl(mTableB, aTable); 1941 break; 1942 case B8G8R8A8_COMPONENT_BYTEOFFSET_A: 1943 FillLookupTableImpl(mTableA, aTable); 1944 break; 1945 default: 1946 MOZ_ASSERT(false, "unknown component"); 1947 break; 1948 } 1949 } 1950 1951 void FilterNodeTableTransferSoftware::FillLookupTableImpl( 1952 const std::vector<Float>& aTableValues, uint8_t aTable[256]) { 1953 uint32_t tvLength = aTableValues.size(); 1954 if (tvLength < 2) { 1955 return; 1956 } 1957 1958 for (size_t i = 0; i < 256; i++) { 1959 uint32_t k = (i * (tvLength - 1)) / 255; 1960 Float v1 = aTableValues[k]; 1961 Float v2 = aTableValues[std::min(k + 1, tvLength - 1)]; 1962 int32_t val = int32_t(255 * (v1 + (i / 255.0f - k / float(tvLength - 1)) * 1963 (tvLength - 1) * (v2 - v1))); 1964 aTable[i] = std::clamp(val, 0, 255); 1965 } 1966 } 1967 1968 void FilterNodeDiscreteTransferSoftware::SetAttribute(uint32_t aIndex, 1969 const Float* aFloat, 1970 uint32_t aSize) { 1971 std::vector<Float> discrete(aFloat, aFloat + aSize); 1972 switch (aIndex) { 1973 case ATT_DISCRETE_TRANSFER_TABLE_R: 1974 mTableR = discrete; 1975 break; 1976 case ATT_DISCRETE_TRANSFER_TABLE_G: 1977 mTableG = discrete; 1978 break; 1979 case ATT_DISCRETE_TRANSFER_TABLE_B: 1980 mTableB = discrete; 1981 break; 1982 case ATT_DISCRETE_TRANSFER_TABLE_A: 1983 mTableA = discrete; 1984 break; 1985 default: 1986 MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute"); 1987 } 1988 Invalidate(); 1989 } 1990 1991 void FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent, 1992 uint8_t aTable[256]) { 1993 switch (aComponent) { 1994 case B8G8R8A8_COMPONENT_BYTEOFFSET_R: 1995 FillLookupTableImpl(mTableR, aTable); 1996 break; 1997 case B8G8R8A8_COMPONENT_BYTEOFFSET_G: 1998 FillLookupTableImpl(mTableG, aTable); 1999 break; 2000 case B8G8R8A8_COMPONENT_BYTEOFFSET_B: 2001 FillLookupTableImpl(mTableB, aTable); 2002 break; 2003 case B8G8R8A8_COMPONENT_BYTEOFFSET_A: 2004 FillLookupTableImpl(mTableA, aTable); 2005 break; 2006 default: 2007 MOZ_ASSERT(false, "unknown component"); 2008 break; 2009 } 2010 } 2011 2012 void FilterNodeDiscreteTransferSoftware::FillLookupTableImpl( 2013 const std::vector<Float>& aTableValues, uint8_t aTable[256]) { 2014 uint32_t tvLength = aTableValues.size(); 2015 if (tvLength < 1) { 2016 return; 2017 } 2018 2019 for (size_t i = 0; i < 256; i++) { 2020 uint32_t k = (i * tvLength) / 255; 2021 k = std::min(k, tvLength - 1); 2022 Float v = aTableValues[k]; 2023 int32_t val = NS_lround(255 * v); 2024 aTable[i] = std::clamp(val, 0, 255); 2025 } 2026 } 2027 2028 FilterNodeLinearTransferSoftware::FilterNodeLinearTransferSoftware() 2029 : mSlopeR(0), 2030 mSlopeG(0), 2031 mSlopeB(0), 2032 mSlopeA(0), 2033 mInterceptR(0), 2034 mInterceptG(0), 2035 mInterceptB(0), 2036 mInterceptA(0) {} 2037 2038 void FilterNodeLinearTransferSoftware::SetAttribute(uint32_t aIndex, 2039 Float aValue) { 2040 switch (aIndex) { 2041 case ATT_LINEAR_TRANSFER_SLOPE_R: 2042 mSlopeR = aValue; 2043 break; 2044 case ATT_LINEAR_TRANSFER_INTERCEPT_R: 2045 mInterceptR = aValue; 2046 break; 2047 case ATT_LINEAR_TRANSFER_SLOPE_G: 2048 mSlopeG = aValue; 2049 break; 2050 case ATT_LINEAR_TRANSFER_INTERCEPT_G: 2051 mInterceptG = aValue; 2052 break; 2053 case ATT_LINEAR_TRANSFER_SLOPE_B: 2054 mSlopeB = aValue; 2055 break; 2056 case ATT_LINEAR_TRANSFER_INTERCEPT_B: 2057 mInterceptB = aValue; 2058 break; 2059 case ATT_LINEAR_TRANSFER_SLOPE_A: 2060 mSlopeA = aValue; 2061 break; 2062 case ATT_LINEAR_TRANSFER_INTERCEPT_A: 2063 mInterceptA = aValue; 2064 break; 2065 default: 2066 MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute"); 2067 } 2068 Invalidate(); 2069 } 2070 2071 void FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent, 2072 uint8_t aTable[256]) { 2073 switch (aComponent) { 2074 case B8G8R8A8_COMPONENT_BYTEOFFSET_R: 2075 FillLookupTableImpl(mSlopeR, mInterceptR, aTable); 2076 break; 2077 case B8G8R8A8_COMPONENT_BYTEOFFSET_G: 2078 FillLookupTableImpl(mSlopeG, mInterceptG, aTable); 2079 break; 2080 case B8G8R8A8_COMPONENT_BYTEOFFSET_B: 2081 FillLookupTableImpl(mSlopeB, mInterceptB, aTable); 2082 break; 2083 case B8G8R8A8_COMPONENT_BYTEOFFSET_A: 2084 FillLookupTableImpl(mSlopeA, mInterceptA, aTable); 2085 break; 2086 default: 2087 MOZ_ASSERT(false, "unknown component"); 2088 break; 2089 } 2090 } 2091 2092 void FilterNodeLinearTransferSoftware::FillLookupTableImpl( 2093 Float aSlope, Float aIntercept, uint8_t aTable[256]) { 2094 for (size_t i = 0; i < 256; i++) { 2095 int32_t val = NS_lround(aSlope * i + 255 * aIntercept); 2096 aTable[i] = std::clamp(val, 0, 255); 2097 } 2098 } 2099 2100 FilterNodeGammaTransferSoftware::FilterNodeGammaTransferSoftware() 2101 : mAmplitudeR(0), 2102 mAmplitudeG(0), 2103 mAmplitudeB(0), 2104 mAmplitudeA(0), 2105 mExponentR(0), 2106 mExponentG(0), 2107 mExponentB(0), 2108 mExponentA(0), 2109 mOffsetR(0.0), 2110 mOffsetG(0.0), 2111 mOffsetB(0.0), 2112 mOffsetA(0.0) {} 2113 2114 void FilterNodeGammaTransferSoftware::SetAttribute(uint32_t aIndex, 2115 Float aValue) { 2116 switch (aIndex) { 2117 case ATT_GAMMA_TRANSFER_AMPLITUDE_R: 2118 mAmplitudeR = aValue; 2119 break; 2120 case ATT_GAMMA_TRANSFER_EXPONENT_R: 2121 mExponentR = aValue; 2122 break; 2123 case ATT_GAMMA_TRANSFER_OFFSET_R: 2124 mOffsetR = aValue; 2125 break; 2126 case ATT_GAMMA_TRANSFER_AMPLITUDE_G: 2127 mAmplitudeG = aValue; 2128 break; 2129 case ATT_GAMMA_TRANSFER_EXPONENT_G: 2130 mExponentG = aValue; 2131 break; 2132 case ATT_GAMMA_TRANSFER_OFFSET_G: 2133 mOffsetG = aValue; 2134 break; 2135 case ATT_GAMMA_TRANSFER_AMPLITUDE_B: 2136 mAmplitudeB = aValue; 2137 break; 2138 case ATT_GAMMA_TRANSFER_EXPONENT_B: 2139 mExponentB = aValue; 2140 break; 2141 case ATT_GAMMA_TRANSFER_OFFSET_B: 2142 mOffsetB = aValue; 2143 break; 2144 case ATT_GAMMA_TRANSFER_AMPLITUDE_A: 2145 mAmplitudeA = aValue; 2146 break; 2147 case ATT_GAMMA_TRANSFER_EXPONENT_A: 2148 mExponentA = aValue; 2149 break; 2150 case ATT_GAMMA_TRANSFER_OFFSET_A: 2151 mOffsetA = aValue; 2152 break; 2153 default: 2154 MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute"); 2155 } 2156 Invalidate(); 2157 } 2158 2159 void FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent, 2160 uint8_t aTable[256]) { 2161 switch (aComponent) { 2162 case B8G8R8A8_COMPONENT_BYTEOFFSET_R: 2163 FillLookupTableImpl(mAmplitudeR, mExponentR, mOffsetR, aTable); 2164 break; 2165 case B8G8R8A8_COMPONENT_BYTEOFFSET_G: 2166 FillLookupTableImpl(mAmplitudeG, mExponentG, mOffsetG, aTable); 2167 break; 2168 case B8G8R8A8_COMPONENT_BYTEOFFSET_B: 2169 FillLookupTableImpl(mAmplitudeB, mExponentB, mOffsetB, aTable); 2170 break; 2171 case B8G8R8A8_COMPONENT_BYTEOFFSET_A: 2172 FillLookupTableImpl(mAmplitudeA, mExponentA, mOffsetA, aTable); 2173 break; 2174 default: 2175 MOZ_ASSERT(false, "unknown component"); 2176 break; 2177 } 2178 } 2179 2180 void FilterNodeGammaTransferSoftware::FillLookupTableImpl(Float aAmplitude, 2181 Float aExponent, 2182 Float aOffset, 2183 uint8_t aTable[256]) { 2184 for (size_t i = 0; i < 256; i++) { 2185 int32_t val = 2186 NS_lround(255 * (aAmplitude * pow(i / 255.0f, aExponent) + aOffset)); 2187 aTable[i] = std::clamp(val, 0, 255); 2188 } 2189 } 2190 2191 FilterNodeConvolveMatrixSoftware::FilterNodeConvolveMatrixSoftware() 2192 : mDivisor(0), 2193 mBias(0), 2194 mEdgeMode(EDGE_MODE_DUPLICATE), 2195 mPreserveAlpha(false) {} 2196 2197 int32_t FilterNodeConvolveMatrixSoftware::InputIndex(uint32_t aInputEnumIndex) { 2198 switch (aInputEnumIndex) { 2199 case IN_CONVOLVE_MATRIX_IN: 2200 return 0; 2201 default: 2202 return -1; 2203 } 2204 } 2205 2206 void FilterNodeConvolveMatrixSoftware::SetAttribute( 2207 uint32_t aIndex, const IntSize& aKernelSize) { 2208 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE); 2209 mKernelSize = aKernelSize; 2210 Invalidate(); 2211 } 2212 2213 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, 2214 const Float* aMatrix, 2215 uint32_t aSize) { 2216 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX); 2217 mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize); 2218 Invalidate(); 2219 } 2220 2221 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, 2222 Float aValue) { 2223 switch (aIndex) { 2224 case ATT_CONVOLVE_MATRIX_DIVISOR: 2225 mDivisor = aValue; 2226 break; 2227 case ATT_CONVOLVE_MATRIX_BIAS: 2228 mBias = aValue; 2229 break; 2230 default: 2231 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute"); 2232 } 2233 Invalidate(); 2234 } 2235 2236 void FilterNodeConvolveMatrixSoftware::SetAttribute( 2237 uint32_t aIndex, const Size& aKernelUnitLength) { 2238 switch (aIndex) { 2239 case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH: 2240 mKernelUnitLength = aKernelUnitLength; 2241 break; 2242 default: 2243 MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute"); 2244 } 2245 Invalidate(); 2246 } 2247 2248 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, 2249 const IntPoint& aTarget) { 2250 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET); 2251 mTarget = aTarget; 2252 Invalidate(); 2253 } 2254 2255 void FilterNodeConvolveMatrixSoftware::SetAttribute( 2256 uint32_t aIndex, const IntRect& aRenderRect) { 2257 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_RENDER_RECT); 2258 mRenderRect = aRenderRect; 2259 Invalidate(); 2260 } 2261 2262 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, 2263 uint32_t aEdgeMode) { 2264 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE); 2265 mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode); 2266 Invalidate(); 2267 } 2268 2269 void FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, 2270 bool aPreserveAlpha) { 2271 MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA); 2272 mPreserveAlpha = aPreserveAlpha; 2273 Invalidate(); 2274 } 2275 2276 #ifdef DEBUG 2277 static inline void DebugOnlyCheckColorSamplingAccess( 2278 const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin, 2279 const uint8_t* aBoundsEnd) { 2280 MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start"); 2281 MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end"); 2282 } 2283 #else 2284 # define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd) 2285 #endif 2286 2287 static inline uint8_t ColorComponentAtPoint(const uint8_t* aData, 2288 ptrdiff_t aStride, 2289 const uint8_t* aBoundsBegin, 2290 const uint8_t* aBoundsEnd, 2291 int32_t x, int32_t y, ptrdiff_t bpp, 2292 ptrdiff_t c) { 2293 DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c], 2294 aBoundsBegin, aBoundsEnd); 2295 return aData[y * aStride + bpp * x + c]; 2296 } 2297 2298 static inline int32_t ColorAtPoint(const uint8_t* aData, ptrdiff_t aStride, 2299 const uint8_t* aBoundsBegin, 2300 const uint8_t* aBoundsEnd, int32_t x, 2301 int32_t y) { 2302 DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin, 2303 aBoundsEnd); 2304 return *(uint32_t*)(aData + y * aStride + 4 * x); 2305 } 2306 2307 // Accepts fractional x & y and does bilinear interpolation. 2308 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible. 2309 static inline uint8_t ColorComponentAtPoint( 2310 const uint8_t* aData, ptrdiff_t aStride, const uint8_t* aBoundsBegin, 2311 const uint8_t* aBoundsEnd, Float x, Float y, ptrdiff_t bpp, ptrdiff_t c) { 2312 const uint32_t f = 256; 2313 const int32_t lx = floor(x); 2314 const int32_t ly = floor(y); 2315 const int32_t tux = uint32_t((x - lx) * f); 2316 const int32_t tlx = f - tux; 2317 const int32_t tuy = uint32_t((y - ly) * f); 2318 const int32_t tly = f - tuy; 2319 const uint8_t& cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin, 2320 aBoundsEnd, lx, ly, bpp, c); 2321 const uint8_t& cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin, 2322 aBoundsEnd, lx + 1, ly, bpp, c); 2323 const uint8_t& clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin, 2324 aBoundsEnd, lx, ly + 1, bpp, c); 2325 const uint8_t& cuu = ColorComponentAtPoint( 2326 aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c); 2327 return ((cll * tlx + cul * tux) * tly + (clu * tlx + cuu * tux) * tuy + 2328 f * f / 2) / 2329 (f * f); 2330 } 2331 2332 static int32_t ClampToNonZero(int32_t a) { return a * (a >= 0); } 2333 2334 template <typename CoordType> 2335 static void ConvolvePixel(const uint8_t* aSourceData, uint8_t* aTargetData, 2336 int32_t aWidth, int32_t aHeight, 2337 int32_t aSourceStride, int32_t aTargetStride, 2338 const uint8_t* aSourceBegin, 2339 const uint8_t* aSourceEnd, int32_t aX, int32_t aY, 2340 const int32_t* aKernel, int32_t aBias, int32_t shiftL, 2341 int32_t shiftR, bool aPreserveAlpha, int32_t aOrderX, 2342 int32_t aOrderY, int32_t aTargetX, int32_t aTargetY, 2343 CoordType aKernelUnitLengthX, 2344 CoordType aKernelUnitLengthY) { 2345 int32_t sum[4] = {0, 0, 0, 0}; 2346 int32_t offsets[4] = { 2347 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G, 2348 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A}; 2349 int32_t channels = aPreserveAlpha ? 3 : 4; 2350 int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1); 2351 2352 for (int32_t y = 0; y < aOrderY; y++) { 2353 CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY; 2354 for (int32_t x = 0; x < aOrderX; x++) { 2355 CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX; 2356 for (int32_t i = 0; i < channels; i++) { 2357 sum[i] += 2358 aKernel[aOrderX * y + x] * 2359 ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin, 2360 aSourceEnd, sampleX, sampleY, 4, offsets[i]); 2361 } 2362 } 2363 } 2364 for (int32_t i = 0; i < channels; i++) { 2365 int32_t clamped = 2366 umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR); 2367 aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] = 2368 (clamped + roundingAddition) << shiftR >> shiftL; 2369 } 2370 if (aPreserveAlpha) { 2371 aTargetData[aY * aTargetStride + 4 * aX + B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 2372 aSourceData[aY * aSourceStride + 4 * aX + 2373 B8G8R8A8_COMPONENT_BYTEOFFSET_A]; 2374 } 2375 } 2376 2377 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::Render( 2378 const IntRect& aRect) { 2379 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && 2380 mKernelUnitLength.height == floor(mKernelUnitLength.height)) { 2381 return DoRender(aRect, (int32_t)mKernelUnitLength.width, 2382 (int32_t)mKernelUnitLength.height); 2383 } 2384 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); 2385 } 2386 2387 static std::vector<Float> ReversedVector(const std::vector<Float>& aVector) { 2388 size_t length = aVector.size(); 2389 std::vector<Float> result(length, 0); 2390 for (size_t i = 0; i < length; i++) { 2391 result[length - 1 - i] = aVector[i]; 2392 } 2393 return result; 2394 } 2395 2396 static std::vector<Float> ScaledVector(const std::vector<Float>& aVector, 2397 Float aDivisor) { 2398 size_t length = aVector.size(); 2399 std::vector<Float> result(length, 0); 2400 for (size_t i = 0; i < length; i++) { 2401 result[i] = aVector[i] / aDivisor; 2402 } 2403 return result; 2404 } 2405 2406 static Float MaxVectorSum(const std::vector<Float>& aVector) { 2407 Float sum = 0; 2408 size_t length = aVector.size(); 2409 for (size_t i = 0; i < length; i++) { 2410 if (aVector[i] > 0) { 2411 sum += aVector[i]; 2412 } 2413 } 2414 return sum; 2415 } 2416 2417 // Returns shiftL and shiftR in such a way that 2418 // a << shiftL >> shiftR is roughly a * aFloat. 2419 static void TranslateDoubleToShifts(double aDouble, int32_t& aShiftL, 2420 int32_t& aShiftR) { 2421 aShiftL = 0; 2422 aShiftR = 0; 2423 if (aDouble <= 0) { 2424 MOZ_CRASH("GFX: TranslateDoubleToShifts"); 2425 } 2426 if (aDouble < 1) { 2427 while (1 << (aShiftR + 1) < 1 / aDouble) { 2428 aShiftR++; 2429 } 2430 } else { 2431 while (1 << (aShiftL + 1) < aDouble) { 2432 aShiftL++; 2433 } 2434 } 2435 } 2436 2437 template <typename CoordType> 2438 already_AddRefed<DataSourceSurface> FilterNodeConvolveMatrixSoftware::DoRender( 2439 const IntRect& aRect, CoordType aKernelUnitLengthX, 2440 CoordType aKernelUnitLengthY) { 2441 if (mKernelSize.width <= 0 || mKernelSize.height <= 0 || 2442 mKernelMatrix.size() != 2443 uint32_t(mKernelSize.width * mKernelSize.height) || 2444 !IntRect(IntPoint(0, 0), mKernelSize).Contains(mTarget) || 2445 mDivisor == 0) { 2446 return Factory::CreateDataSourceSurface(aRect.Size(), 2447 SurfaceFormat::B8G8R8A8, true); 2448 } 2449 2450 IntRect srcRect = InflatedSourceRect(aRect); 2451 2452 // Inflate the source rect by another pixel because the bilinear filtering in 2453 // ColorComponentAtPoint may want to access the margins. 2454 srcRect.Inflate(1); 2455 2456 RefPtr<DataSourceSurface> input = 2457 GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, 2458 NEED_COLOR_CHANNELS, mEdgeMode, &mRenderRect); 2459 2460 if (!input) { 2461 return nullptr; 2462 } 2463 2464 RefPtr<DataSourceSurface> target = Factory::CreateDataSourceSurface( 2465 aRect.Size(), SurfaceFormat::B8G8R8A8, true); 2466 if (MOZ2D_WARN_IF(!target)) { 2467 return nullptr; 2468 } 2469 2470 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); 2471 2472 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ); 2473 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 2474 if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) { 2475 return nullptr; 2476 } 2477 2478 uint8_t* sourceData = 2479 DataAtOffset(input, sourceMap.GetMappedSurface(), offset); 2480 int32_t sourceStride = sourceMap.GetStride(); 2481 uint8_t* sourceBegin = sourceMap.GetData(); 2482 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height; 2483 uint8_t* targetData = targetMap.GetData(); 2484 int32_t targetStride = targetMap.GetStride(); 2485 2486 // Why exactly are we reversing the kernel? 2487 std::vector<Float> kernel = ReversedVector(mKernelMatrix); 2488 kernel = ScaledVector(kernel, mDivisor); 2489 Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias, 2490 MaxVectorSum(ScaledVector(kernel, -1)) - mBias); 2491 maxResultAbs = std::max(maxResultAbs, 1.0f); 2492 2493 double idealFactor = INT32_MAX / 2.0 / maxResultAbs / 255.0 * 0.999; 2494 MOZ_ASSERT(255.0 * maxResultAbs * idealFactor <= INT32_MAX / 2.0, 2495 "badly chosen float-to-int scale"); 2496 int32_t shiftL, shiftR; 2497 TranslateDoubleToShifts(idealFactor, shiftL, shiftR); 2498 double factorFromShifts = Float(1 << shiftL) / Float(1 << shiftR); 2499 MOZ_ASSERT(255.0 * maxResultAbs * factorFromShifts <= INT32_MAX / 2.0, 2500 "badly chosen float-to-int scale"); 2501 2502 int32_t* intKernel = new int32_t[kernel.size()]; 2503 for (size_t i = 0; i < kernel.size(); i++) { 2504 intKernel[i] = NS_lround(kernel[i] * factorFromShifts); 2505 } 2506 int32_t bias = NS_lround(mBias * 255 * factorFromShifts); 2507 2508 for (int32_t y = 0; y < aRect.Height(); y++) { 2509 for (int32_t x = 0; x < aRect.Width(); x++) { 2510 ConvolvePixel(sourceData, targetData, aRect.Width(), aRect.Height(), 2511 sourceStride, targetStride, sourceBegin, sourceEnd, x, y, 2512 intKernel, bias, shiftL, shiftR, mPreserveAlpha, 2513 mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y, 2514 aKernelUnitLengthX, aKernelUnitLengthY); 2515 } 2516 } 2517 delete[] intKernel; 2518 2519 return target.forget(); 2520 } 2521 2522 void FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect( 2523 const IntRect& aRect) { 2524 RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect)); 2525 } 2526 2527 IntRect FilterNodeConvolveMatrixSoftware::MapRectToSource( 2528 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 2529 return MapInputRectToSource(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect), 2530 aMax, aSourceNode); 2531 } 2532 2533 IntRect FilterNodeConvolveMatrixSoftware::InflatedSourceRect( 2534 const IntRect& aDestRect) { 2535 if (aDestRect.IsEmpty()) { 2536 return IntRect(); 2537 } 2538 2539 IntMargin margin; 2540 margin.left = static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width)); 2541 margin.top = static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height)); 2542 margin.right = static_cast<int32_t>( 2543 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width)); 2544 margin.bottom = static_cast<int32_t>( 2545 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height)); 2546 2547 IntRect srcRect = aDestRect; 2548 srcRect.Inflate(margin); 2549 return srcRect; 2550 } 2551 2552 IntRect FilterNodeConvolveMatrixSoftware::InflatedDestRect( 2553 const IntRect& aSourceRect) { 2554 if (aSourceRect.IsEmpty()) { 2555 return IntRect(); 2556 } 2557 2558 IntMargin margin; 2559 margin.left = static_cast<int32_t>( 2560 ceil((mKernelSize.width - mTarget.x - 1) * mKernelUnitLength.width)); 2561 margin.top = static_cast<int32_t>( 2562 ceil((mKernelSize.height - mTarget.y - 1) * mKernelUnitLength.height)); 2563 margin.right = 2564 static_cast<int32_t>(ceil(mTarget.x * mKernelUnitLength.width)); 2565 margin.bottom = 2566 static_cast<int32_t>(ceil(mTarget.y * mKernelUnitLength.height)); 2567 2568 IntRect destRect = aSourceRect; 2569 destRect.Inflate(margin); 2570 return destRect; 2571 } 2572 2573 IntRect FilterNodeConvolveMatrixSoftware::GetOutputRectInRect( 2574 const IntRect& aRect) { 2575 if (!mPreserveAlpha && mBias > 0) { 2576 // we transform transparent colors into non-transparent colors in this case 2577 return aRect; 2578 } 2579 IntRect srcRequest = InflatedSourceRect(aRect); 2580 IntRect srcOutput = GetInputRectInRect(IN_CONVOLVE_MATRIX_IN, srcRequest); 2581 return InflatedDestRect(srcOutput).Intersect(aRect); 2582 } 2583 2584 FilterNodeDisplacementMapSoftware::FilterNodeDisplacementMapSoftware() 2585 : mScale(0.0f), mChannelX(COLOR_CHANNEL_R), mChannelY(COLOR_CHANNEL_G) {} 2586 2587 int32_t FilterNodeDisplacementMapSoftware::InputIndex( 2588 uint32_t aInputEnumIndex) { 2589 switch (aInputEnumIndex) { 2590 case IN_DISPLACEMENT_MAP_IN: 2591 return 0; 2592 case IN_DISPLACEMENT_MAP_IN2: 2593 return 1; 2594 default: 2595 return -1; 2596 } 2597 } 2598 2599 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, 2600 Float aScale) { 2601 MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE); 2602 mScale = aScale; 2603 Invalidate(); 2604 } 2605 2606 void FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, 2607 uint32_t aValue) { 2608 switch (aIndex) { 2609 case ATT_DISPLACEMENT_MAP_X_CHANNEL: 2610 mChannelX = static_cast<ColorChannel>(aValue); 2611 break; 2612 case ATT_DISPLACEMENT_MAP_Y_CHANNEL: 2613 mChannelY = static_cast<ColorChannel>(aValue); 2614 break; 2615 default: 2616 MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute"); 2617 } 2618 Invalidate(); 2619 } 2620 2621 already_AddRefed<DataSourceSurface> FilterNodeDisplacementMapSoftware::Render( 2622 const IntRect& aRect) { 2623 IntRect srcRect = InflatedSourceOrDestRect(aRect); 2624 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface( 2625 IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS); 2626 RefPtr<DataSourceSurface> map = GetInputDataSourceSurface( 2627 IN_DISPLACEMENT_MAP_IN2, aRect, NEED_COLOR_CHANNELS); 2628 RefPtr<DataSourceSurface> target = 2629 Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8); 2630 if (MOZ2D_WARN_IF(!(input && map && target))) { 2631 return nullptr; 2632 } 2633 2634 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); 2635 2636 DataSourceSurface::ScopedMap inputMap(input, DataSourceSurface::READ); 2637 DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ); 2638 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 2639 if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && 2640 targetMap.IsMapped()))) { 2641 return nullptr; 2642 } 2643 2644 uint8_t* sourceData = 2645 DataAtOffset(input, inputMap.GetMappedSurface(), offset); 2646 int32_t sourceStride = inputMap.GetStride(); 2647 uint8_t* sourceBegin = inputMap.GetData(); 2648 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height; 2649 uint8_t* mapData = mapMap.GetData(); 2650 int32_t mapStride = mapMap.GetStride(); 2651 uint8_t* targetData = targetMap.GetData(); 2652 int32_t targetStride = targetMap.GetStride(); 2653 2654 static const ptrdiff_t channelMap[4] = { 2655 B8G8R8A8_COMPONENT_BYTEOFFSET_R, B8G8R8A8_COMPONENT_BYTEOFFSET_G, 2656 B8G8R8A8_COMPONENT_BYTEOFFSET_B, B8G8R8A8_COMPONENT_BYTEOFFSET_A}; 2657 uint16_t xChannel = channelMap[mChannelX]; 2658 uint16_t yChannel = channelMap[mChannelY]; 2659 2660 float scaleOver255 = mScale / 255.0f; 2661 float scaleAdjustment = -0.5f * mScale; 2662 2663 for (int32_t y = 0; y < aRect.Height(); y++) { 2664 for (int32_t x = 0; x < aRect.Width(); x++) { 2665 uint32_t mapIndex = y * mapStride + 4 * x; 2666 uint32_t targIndex = y * targetStride + 4 * x; 2667 int32_t sourceX = 2668 x + scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment; 2669 int32_t sourceY = 2670 y + scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment; 2671 *(uint32_t*)(targetData + targIndex) = ColorAtPoint( 2672 sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY); 2673 } 2674 2675 // Keep valgrind happy. 2676 PodZero(&targetData[y * targetStride + 4 * aRect.Width()], 2677 targetStride - 4 * aRect.Width()); 2678 } 2679 2680 return target.forget(); 2681 } 2682 2683 void FilterNodeDisplacementMapSoftware::RequestFromInputsForRect( 2684 const IntRect& aRect) { 2685 RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect)); 2686 RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect); 2687 } 2688 2689 IntRect FilterNodeDisplacementMapSoftware::MapRectToSource( 2690 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 2691 IntRect result = 2692 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN, 2693 InflatedSourceOrDestRect(aRect), aMax, aSourceNode); 2694 result.OrWith( 2695 MapInputRectToSource(IN_DISPLACEMENT_MAP_IN2, aRect, aMax, aSourceNode)); 2696 return result; 2697 } 2698 2699 IntRect FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect( 2700 const IntRect& aDestOrSourceRect) { 2701 IntRect sourceOrDestRect = aDestOrSourceRect; 2702 sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2)); 2703 return sourceOrDestRect; 2704 } 2705 2706 IntRect FilterNodeDisplacementMapSoftware::GetOutputRectInRect( 2707 const IntRect& aRect) { 2708 IntRect srcRequest = InflatedSourceOrDestRect(aRect); 2709 IntRect srcOutput = GetInputRectInRect(IN_DISPLACEMENT_MAP_IN, srcRequest); 2710 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); 2711 } 2712 2713 FilterNodeTurbulenceSoftware::FilterNodeTurbulenceSoftware() 2714 : mNumOctaves(0), 2715 mSeed(0), 2716 mStitchable(false), 2717 mType(TURBULENCE_TYPE_TURBULENCE) {} 2718 2719 int32_t FilterNodeTurbulenceSoftware::InputIndex(uint32_t aInputEnumIndex) { 2720 return -1; 2721 } 2722 2723 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, 2724 const Size& aBaseFrequency) { 2725 switch (aIndex) { 2726 case ATT_TURBULENCE_BASE_FREQUENCY: 2727 mBaseFrequency = aBaseFrequency; 2728 break; 2729 default: 2730 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute"); 2731 break; 2732 } 2733 Invalidate(); 2734 } 2735 2736 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, 2737 const IntRect& aRect) { 2738 switch (aIndex) { 2739 case ATT_TURBULENCE_RECT: 2740 mRenderRect = aRect; 2741 break; 2742 default: 2743 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute"); 2744 break; 2745 } 2746 Invalidate(); 2747 } 2748 2749 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, 2750 bool aStitchable) { 2751 MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE); 2752 mStitchable = aStitchable; 2753 Invalidate(); 2754 } 2755 2756 void FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, 2757 uint32_t aValue) { 2758 switch (aIndex) { 2759 case ATT_TURBULENCE_NUM_OCTAVES: 2760 mNumOctaves = aValue; 2761 break; 2762 case ATT_TURBULENCE_SEED: 2763 mSeed = aValue; 2764 break; 2765 case ATT_TURBULENCE_TYPE: 2766 mType = static_cast<TurbulenceType>(aValue); 2767 break; 2768 default: 2769 MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute"); 2770 break; 2771 } 2772 Invalidate(); 2773 } 2774 2775 already_AddRefed<DataSourceSurface> FilterNodeTurbulenceSoftware::Render( 2776 const IntRect& aRect) { 2777 return FilterProcessing::RenderTurbulence( 2778 aRect.Size(), aRect.TopLeft(), mBaseFrequency, mSeed, mNumOctaves, mType, 2779 mStitchable, Rect(mRenderRect)); 2780 } 2781 2782 IntRect FilterNodeTurbulenceSoftware::GetOutputRectInRect( 2783 const IntRect& aRect) { 2784 return aRect.Intersect(mRenderRect); 2785 } 2786 2787 IntRect FilterNodeTurbulenceSoftware::MapRectToSource(const IntRect& aRect, 2788 const IntRect& aMax, 2789 FilterNode* aSourceNode) { 2790 return IntRect(); 2791 } 2792 2793 FilterNodeArithmeticCombineSoftware::FilterNodeArithmeticCombineSoftware() 2794 : mK1(0), mK2(0), mK3(0), mK4(0) {} 2795 2796 int32_t FilterNodeArithmeticCombineSoftware::InputIndex( 2797 uint32_t aInputEnumIndex) { 2798 switch (aInputEnumIndex) { 2799 case IN_ARITHMETIC_COMBINE_IN: 2800 return 0; 2801 case IN_ARITHMETIC_COMBINE_IN2: 2802 return 1; 2803 default: 2804 return -1; 2805 } 2806 } 2807 2808 void FilterNodeArithmeticCombineSoftware::SetAttribute(uint32_t aIndex, 2809 const Float* aFloat, 2810 uint32_t aSize) { 2811 MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS); 2812 MOZ_RELEASE_ASSERT(aSize == 4); 2813 2814 mK1 = aFloat[0]; 2815 mK2 = aFloat[1]; 2816 mK3 = aFloat[2]; 2817 mK4 = aFloat[3]; 2818 2819 Invalidate(); 2820 } 2821 2822 already_AddRefed<DataSourceSurface> FilterNodeArithmeticCombineSoftware::Render( 2823 const IntRect& aRect) { 2824 RefPtr<DataSourceSurface> input1 = GetInputDataSourceSurface( 2825 IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS); 2826 RefPtr<DataSourceSurface> input2 = GetInputDataSourceSurface( 2827 IN_ARITHMETIC_COMBINE_IN2, aRect, NEED_COLOR_CHANNELS); 2828 if (!input1 && !input2) { 2829 return nullptr; 2830 } 2831 2832 // If one input is null, treat it as transparent by adjusting the factors. 2833 Float k1 = mK1, k2 = mK2, k3 = mK3, k4 = mK4; 2834 if (!input1) { 2835 k1 = 0.0f; 2836 k2 = 0.0f; 2837 input1 = input2; 2838 } 2839 2840 if (!input2) { 2841 k1 = 0.0f; 2842 k3 = 0.0f; 2843 input2 = input1; 2844 } 2845 2846 return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, 2847 k4); 2848 } 2849 2850 void FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect( 2851 const IntRect& aRect) { 2852 RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect); 2853 RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect); 2854 } 2855 2856 IntRect FilterNodeArithmeticCombineSoftware::MapRectToSource( 2857 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 2858 IntRect result = 2859 MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN, aRect, aMax, aSourceNode); 2860 result.OrWith(MapInputRectToSource(IN_ARITHMETIC_COMBINE_IN2, aRect, aMax, 2861 aSourceNode)); 2862 return result; 2863 } 2864 2865 IntRect FilterNodeArithmeticCombineSoftware::GetOutputRectInRect( 2866 const IntRect& aRect) { 2867 if (mK4 > 0.0f) { 2868 return aRect; 2869 } 2870 IntRect rectFrom1 = 2871 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect); 2872 IntRect rectFrom2 = 2873 GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect); 2874 IntRect result; 2875 if (mK1 > 0.0f) { 2876 result = rectFrom1.Intersect(rectFrom2); 2877 } 2878 if (mK2 > 0.0f) { 2879 result = result.Union(rectFrom1); 2880 } 2881 if (mK3 > 0.0f) { 2882 result = result.Union(rectFrom2); 2883 } 2884 return result; 2885 } 2886 2887 FilterNodeCompositeSoftware::FilterNodeCompositeSoftware() 2888 : mOperator(COMPOSITE_OPERATOR_OVER) {} 2889 2890 int32_t FilterNodeCompositeSoftware::InputIndex(uint32_t aInputEnumIndex) { 2891 return aInputEnumIndex - IN_COMPOSITE_IN_START; 2892 } 2893 2894 void FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, 2895 uint32_t aCompositeOperator) { 2896 MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR); 2897 mOperator = static_cast<CompositeOperator>(aCompositeOperator); 2898 Invalidate(); 2899 } 2900 2901 already_AddRefed<DataSourceSurface> FilterNodeCompositeSoftware::Render( 2902 const IntRect& aRect) { 2903 RefPtr<DataSourceSurface> start = GetInputDataSourceSurface( 2904 IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS); 2905 RefPtr<DataSourceSurface> dest = Factory::CreateDataSourceSurface( 2906 aRect.Size(), SurfaceFormat::B8G8R8A8, true); 2907 if (MOZ2D_WARN_IF(!dest)) { 2908 return nullptr; 2909 } 2910 2911 if (start) { 2912 CopyRect(start, dest, aRect - aRect.TopLeft(), IntPoint()); 2913 } 2914 2915 for (size_t inputIndex = 1; inputIndex < NumberOfSetInputs(); inputIndex++) { 2916 RefPtr<DataSourceSurface> input = GetInputDataSourceSurface( 2917 IN_COMPOSITE_IN_START + inputIndex, aRect, NEED_COLOR_CHANNELS); 2918 if (input) { 2919 FilterProcessing::ApplyComposition(input, dest, mOperator); 2920 } else { 2921 // We need to treat input as transparent. Depending on the composite 2922 // operator, different things happen to dest. 2923 switch (mOperator) { 2924 case COMPOSITE_OPERATOR_OVER: 2925 case COMPOSITE_OPERATOR_ATOP: 2926 case COMPOSITE_OPERATOR_XOR: 2927 case COMPOSITE_OPERATOR_LIGHTER: 2928 // dest is unchanged. 2929 break; 2930 case COMPOSITE_OPERATOR_OUT: 2931 // dest is now transparent, but it can become non-transparent again 2932 // when compositing additional inputs. 2933 ClearDataSourceSurface(dest); 2934 break; 2935 case COMPOSITE_OPERATOR_IN: 2936 // Transparency always wins. We're completely transparent now and 2937 // no additional input can get rid of that transparency. 2938 return nullptr; 2939 } 2940 } 2941 } 2942 return dest.forget(); 2943 } 2944 2945 void FilterNodeCompositeSoftware::RequestFromInputsForRect( 2946 const IntRect& aRect) { 2947 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { 2948 RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect); 2949 } 2950 } 2951 2952 IntRect FilterNodeCompositeSoftware::MapRectToSource(const IntRect& aRect, 2953 const IntRect& aMax, 2954 FilterNode* aSourceNode) { 2955 IntRect result; 2956 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { 2957 result.OrWith(MapInputRectToSource(IN_COMPOSITE_IN_START + inputIndex, 2958 aRect, aMax, aSourceNode)); 2959 } 2960 return result; 2961 } 2962 2963 IntRect FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect) { 2964 IntRect rect; 2965 for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) { 2966 IntRect inputRect = 2967 GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect); 2968 if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) { 2969 rect = rect.Intersect(inputRect); 2970 } else { 2971 rect = rect.Union(inputRect); 2972 } 2973 } 2974 return rect; 2975 } 2976 2977 int32_t FilterNodeBlurXYSoftware::InputIndex(uint32_t aInputEnumIndex) { 2978 switch (aInputEnumIndex) { 2979 case IN_GAUSSIAN_BLUR_IN: 2980 return 0; 2981 default: 2982 return -1; 2983 } 2984 } 2985 2986 already_AddRefed<DataSourceSurface> FilterNodeBlurXYSoftware::Render( 2987 const IntRect& aRect) { 2988 Size sigmaXY = StdDeviationXY(); 2989 IntSize d = 2990 GaussianBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); 2991 2992 if (d.width == 0 && d.height == 0) { 2993 return GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, aRect); 2994 } 2995 2996 IntRect srcRect = InflatedSourceOrDestRect(aRect); 2997 RefPtr<DataSourceSurface> input = 2998 GetInputDataSourceSurface(IN_GAUSSIAN_BLUR_IN, srcRect); 2999 if (!input) { 3000 return nullptr; 3001 } 3002 3003 RefPtr<DataSourceSurface> target = 3004 Factory::CreateDataSourceSurface(srcRect.Size(), input->GetFormat()); 3005 if (MOZ2D_WARN_IF(!target)) { 3006 return nullptr; 3007 } 3008 CopyRect(input, target, IntRect(IntPoint(), input->GetSize()), IntPoint()); 3009 3010 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::READ_WRITE); 3011 if (MOZ2D_WARN_IF(!targetMap.IsMapped())) { 3012 return nullptr; 3013 } 3014 GaussianBlur blur(Point(sigmaXY.width, sigmaXY.height)); 3015 blur.Blur(targetMap.GetData(), targetMap.GetStride(), target->GetSize(), 3016 target->GetFormat()); 3017 3018 return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE); 3019 } 3020 3021 void FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect& aRect) { 3022 RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect)); 3023 } 3024 3025 IntRect FilterNodeBlurXYSoftware::MapRectToSource(const IntRect& aRect, 3026 const IntRect& aMax, 3027 FilterNode* aSourceNode) { 3028 return MapInputRectToSource( 3029 IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect), aMax, aSourceNode); 3030 } 3031 3032 IntRect FilterNodeBlurXYSoftware::InflatedSourceOrDestRect( 3033 const IntRect& aDestRect) { 3034 Size sigmaXY = StdDeviationXY(); 3035 IntSize d = 3036 GaussianBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height)); 3037 IntRect srcRect = aDestRect; 3038 srcRect.Inflate(d); 3039 return srcRect; 3040 } 3041 3042 IntRect FilterNodeBlurXYSoftware::GetOutputRectInRect(const IntRect& aRect) { 3043 IntRect srcRequest = InflatedSourceOrDestRect(aRect); 3044 IntRect srcOutput = GetInputRectInRect(IN_GAUSSIAN_BLUR_IN, srcRequest); 3045 return InflatedSourceOrDestRect(srcOutput).Intersect(aRect); 3046 } 3047 3048 FilterNodeGaussianBlurSoftware::FilterNodeGaussianBlurSoftware() 3049 : mStdDeviation(0) {} 3050 3051 static float ClampStdDeviation(float aStdDeviation) { 3052 // Cap software blur radius for performance reasons. 3053 return std::clamp(aStdDeviation, 0.f, 100.f); 3054 } 3055 3056 void FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex, 3057 float aStdDeviation) { 3058 switch (aIndex) { 3059 case ATT_GAUSSIAN_BLUR_STD_DEVIATION: 3060 mStdDeviation = ClampStdDeviation(aStdDeviation); 3061 break; 3062 default: 3063 MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute"); 3064 } 3065 Invalidate(); 3066 } 3067 3068 Size FilterNodeGaussianBlurSoftware::StdDeviationXY() { 3069 return Size(mStdDeviation, mStdDeviation); 3070 } 3071 3072 FilterNodeDirectionalBlurSoftware::FilterNodeDirectionalBlurSoftware() 3073 : mStdDeviation(0.0), mBlurDirection(BLUR_DIRECTION_X) {} 3074 3075 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, 3076 Float aStdDeviation) { 3077 switch (aIndex) { 3078 case ATT_DIRECTIONAL_BLUR_STD_DEVIATION: 3079 mStdDeviation = ClampStdDeviation(aStdDeviation); 3080 break; 3081 default: 3082 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute"); 3083 } 3084 Invalidate(); 3085 } 3086 3087 void FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex, 3088 uint32_t aBlurDirection) { 3089 switch (aIndex) { 3090 case ATT_DIRECTIONAL_BLUR_DIRECTION: 3091 mBlurDirection = (BlurDirection)aBlurDirection; 3092 break; 3093 default: 3094 MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute"); 3095 } 3096 Invalidate(); 3097 } 3098 3099 Size FilterNodeDirectionalBlurSoftware::StdDeviationXY() { 3100 float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0; 3101 float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0; 3102 return Size(sigmaX, sigmaY); 3103 } 3104 3105 int32_t FilterNodeCropSoftware::InputIndex(uint32_t aInputEnumIndex) { 3106 switch (aInputEnumIndex) { 3107 case IN_CROP_IN: 3108 return 0; 3109 default: 3110 return -1; 3111 } 3112 } 3113 3114 void FilterNodeCropSoftware::SetAttribute(uint32_t aIndex, 3115 const Rect& aSourceRect) { 3116 MOZ_ASSERT(aIndex == ATT_CROP_RECT); 3117 Rect srcRect = aSourceRect; 3118 srcRect.Round(); 3119 if (!srcRect.ToIntRect(&mCropRect)) { 3120 mCropRect = IntRect(); 3121 } 3122 Invalidate(); 3123 } 3124 3125 already_AddRefed<DataSourceSurface> FilterNodeCropSoftware::Render( 3126 const IntRect& aRect) { 3127 return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect)); 3128 } 3129 3130 void FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect& aRect) { 3131 RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect)); 3132 } 3133 3134 IntRect FilterNodeCropSoftware::MapRectToSource(const IntRect& aRect, 3135 const IntRect& aMax, 3136 FilterNode* aSourceNode) { 3137 return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax, 3138 aSourceNode); 3139 } 3140 3141 IntRect FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect) { 3142 return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect); 3143 } 3144 3145 int32_t FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) { 3146 switch (aInputEnumIndex) { 3147 case IN_PREMULTIPLY_IN: 3148 return 0; 3149 default: 3150 return -1; 3151 } 3152 } 3153 3154 already_AddRefed<DataSourceSurface> FilterNodePremultiplySoftware::Render( 3155 const IntRect& aRect) { 3156 RefPtr<DataSourceSurface> input = 3157 GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect); 3158 return input ? Premultiply(input) : nullptr; 3159 } 3160 3161 void FilterNodePremultiplySoftware::RequestFromInputsForRect( 3162 const IntRect& aRect) { 3163 RequestInputRect(IN_PREMULTIPLY_IN, aRect); 3164 } 3165 3166 IntRect FilterNodePremultiplySoftware::MapRectToSource( 3167 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 3168 return MapInputRectToSource(IN_PREMULTIPLY_IN, aRect, aMax, aSourceNode); 3169 } 3170 3171 IntRect FilterNodePremultiplySoftware::GetOutputRectInRect( 3172 const IntRect& aRect) { 3173 return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect); 3174 } 3175 3176 int32_t FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex) { 3177 switch (aInputEnumIndex) { 3178 case IN_UNPREMULTIPLY_IN: 3179 return 0; 3180 default: 3181 return -1; 3182 } 3183 } 3184 3185 already_AddRefed<DataSourceSurface> FilterNodeUnpremultiplySoftware::Render( 3186 const IntRect& aRect) { 3187 RefPtr<DataSourceSurface> input = 3188 GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect); 3189 return input ? Unpremultiply(input) : nullptr; 3190 } 3191 3192 void FilterNodeUnpremultiplySoftware::RequestFromInputsForRect( 3193 const IntRect& aRect) { 3194 RequestInputRect(IN_UNPREMULTIPLY_IN, aRect); 3195 } 3196 3197 IntRect FilterNodeUnpremultiplySoftware::MapRectToSource( 3198 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 3199 return MapInputRectToSource(IN_UNPREMULTIPLY_IN, aRect, aMax, aSourceNode); 3200 } 3201 3202 IntRect FilterNodeUnpremultiplySoftware::GetOutputRectInRect( 3203 const IntRect& aRect) { 3204 return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect); 3205 } 3206 3207 void FilterNodeOpacitySoftware::SetAttribute(uint32_t aIndex, Float aValue) { 3208 MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE); 3209 mValue = aValue; 3210 Invalidate(); 3211 } 3212 3213 int32_t FilterNodeOpacitySoftware::InputIndex(uint32_t aInputEnumIndex) { 3214 switch (aInputEnumIndex) { 3215 case IN_OPACITY_IN: 3216 return 0; 3217 default: 3218 return -1; 3219 } 3220 } 3221 3222 already_AddRefed<DataSourceSurface> FilterNodeOpacitySoftware::Render( 3223 const IntRect& aRect) { 3224 RefPtr<DataSourceSurface> input = 3225 GetInputDataSourceSurface(IN_OPACITY_IN, aRect); 3226 return input ? Opacity(input, mValue) : nullptr; 3227 } 3228 3229 void FilterNodeOpacitySoftware::RequestFromInputsForRect(const IntRect& aRect) { 3230 RequestInputRect(IN_OPACITY_IN, aRect); 3231 } 3232 3233 IntRect FilterNodeOpacitySoftware::MapRectToSource(const IntRect& aRect, 3234 const IntRect& aMax, 3235 FilterNode* aSourceNode) { 3236 return MapInputRectToSource(IN_OPACITY_IN, aRect, aMax, aSourceNode); 3237 } 3238 3239 IntRect FilterNodeOpacitySoftware::GetOutputRectInRect(const IntRect& aRect) { 3240 return GetInputRectInRect(IN_OPACITY_IN, aRect); 3241 } 3242 3243 bool PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) { 3244 switch (aIndex) { 3245 case ATT_POINT_LIGHT_POSITION: 3246 mPosition = aPoint; 3247 break; 3248 default: 3249 return false; 3250 } 3251 return true; 3252 } 3253 3254 SpotLightSoftware::SpotLightSoftware() 3255 : mSpecularFocus(0), mLimitingConeAngle(0), mLimitingConeCos(1) {} 3256 3257 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, const Point3D& aPoint) { 3258 switch (aIndex) { 3259 case ATT_SPOT_LIGHT_POSITION: 3260 mPosition = aPoint; 3261 break; 3262 case ATT_SPOT_LIGHT_POINTS_AT: 3263 mPointsAt = aPoint; 3264 break; 3265 default: 3266 return false; 3267 } 3268 return true; 3269 } 3270 3271 bool SpotLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) { 3272 switch (aIndex) { 3273 case ATT_SPOT_LIGHT_LIMITING_CONE_ANGLE: 3274 mLimitingConeAngle = aValue; 3275 break; 3276 case ATT_SPOT_LIGHT_FOCUS: 3277 mSpecularFocus = aValue; 3278 break; 3279 default: 3280 return false; 3281 } 3282 return true; 3283 } 3284 3285 DistantLightSoftware::DistantLightSoftware() : mAzimuth(0), mElevation(0) {} 3286 3287 bool DistantLightSoftware::SetAttribute(uint32_t aIndex, Float aValue) { 3288 switch (aIndex) { 3289 case ATT_DISTANT_LIGHT_AZIMUTH: 3290 mAzimuth = aValue; 3291 break; 3292 case ATT_DISTANT_LIGHT_ELEVATION: 3293 mElevation = aValue; 3294 break; 3295 default: 3296 return false; 3297 } 3298 return true; 3299 } 3300 3301 static inline Point3D Normalized(const Point3D& vec) { 3302 Point3D copy(vec); 3303 copy.Normalize(); 3304 return copy; 3305 } 3306 3307 template <typename LightType, typename LightingType> 3308 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware( 3309 const char* aTypeName) 3310 : mSurfaceScale(0) 3311 #if defined(MOZILLA_INTERNAL_API) && defined(NS_BUILD_REFCNT_LOGGING) 3312 , 3313 mTypeName(aTypeName) 3314 #endif 3315 { 3316 } 3317 3318 template <typename LightType, typename LightingType> 3319 int32_t FilterNodeLightingSoftware<LightType, LightingType>::InputIndex( 3320 uint32_t aInputEnumIndex) { 3321 switch (aInputEnumIndex) { 3322 case IN_LIGHTING_IN: 3323 return 0; 3324 default: 3325 return -1; 3326 } 3327 } 3328 3329 template <typename LightType, typename LightingType> 3330 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute( 3331 uint32_t aIndex, const Point3D& aPoint) { 3332 if (mLight.SetAttribute(aIndex, aPoint)) { 3333 Invalidate(); 3334 return; 3335 } 3336 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point"); 3337 } 3338 3339 template <typename LightType, typename LightingType> 3340 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute( 3341 uint32_t aIndex, Float aValue) { 3342 if (mLight.SetAttribute(aIndex, aValue) || 3343 mLighting.SetAttribute(aIndex, aValue)) { 3344 Invalidate(); 3345 return; 3346 } 3347 switch (aIndex) { 3348 case ATT_LIGHTING_SURFACE_SCALE: 3349 mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue; 3350 break; 3351 default: 3352 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float"); 3353 } 3354 Invalidate(); 3355 } 3356 3357 template <typename LightType, typename LightingType> 3358 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute( 3359 uint32_t aIndex, const Size& aKernelUnitLength) { 3360 switch (aIndex) { 3361 case ATT_LIGHTING_KERNEL_UNIT_LENGTH: 3362 mKernelUnitLength = aKernelUnitLength; 3363 break; 3364 default: 3365 MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size"); 3366 } 3367 Invalidate(); 3368 } 3369 3370 template <typename LightType, typename LightingType> 3371 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute( 3372 uint32_t aIndex, const DeviceColor& aColor) { 3373 MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR); 3374 mColor = aColor; 3375 Invalidate(); 3376 } 3377 3378 template <typename LightType, typename LightingType> 3379 void FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute( 3380 uint32_t aIndex, const IntRect& aRenderRect) { 3381 MOZ_ASSERT(aIndex == ATT_LIGHTING_RENDER_RECT); 3382 mRenderRect = aRenderRect; 3383 Invalidate(); 3384 } 3385 3386 template <typename LightType, typename LightingType> 3387 IntRect 3388 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect( 3389 const IntRect& aRect) { 3390 return aRect; 3391 } 3392 3393 Point3D PointLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) { 3394 return Normalized(mPosition - aTargetPoint); 3395 } 3396 3397 uint32_t PointLightSoftware::GetColor(uint32_t aLightColor, 3398 const Point3D& aVectorToLight) { 3399 return aLightColor; 3400 } 3401 3402 void SpotLightSoftware::Prepare() { 3403 mVectorFromFocusPointToLight = Normalized(mPointsAt - mPosition); 3404 mLimitingConeCos = 3405 std::max<double>(cos(mLimitingConeAngle * M_PI / 180.0), 0.0); 3406 mPowCache.CacheForExponent(mSpecularFocus); 3407 } 3408 3409 Point3D SpotLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) { 3410 return Normalized(mPosition - aTargetPoint); 3411 } 3412 3413 uint32_t SpotLightSoftware::GetColor(uint32_t aLightColor, 3414 const Point3D& aVectorToLight) { 3415 union { 3416 uint32_t color; 3417 uint8_t colorC[4]; 3418 }; 3419 3420 Float dot = -aVectorToLight.DotProduct(mVectorFromFocusPointToLight); 3421 if (!mPowCache.HasPowerTable()) { 3422 dot *= (dot >= mLimitingConeCos); 3423 color = aLightColor; 3424 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] *= dot; 3425 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] *= dot; 3426 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] *= dot; 3427 } else { 3428 color = aLightColor; 3429 uint16_t doti = dot * (dot >= 0) * (1 << PowCache::sInputIntPrecisionBits); 3430 uint32_t tmp = mPowCache.Pow(doti) * (dot >= mLimitingConeCos); 3431 MOZ_ASSERT(tmp <= (1 << PowCache::sOutputIntPrecisionBits), 3432 "pow() result must not exceed 1.0"); 3433 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = 3434 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_R] * tmp) >> 3435 PowCache::sOutputIntPrecisionBits); 3436 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = 3437 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_G] * tmp) >> 3438 PowCache::sOutputIntPrecisionBits); 3439 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = 3440 uint8_t((colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_B] * tmp) >> 3441 PowCache::sOutputIntPrecisionBits); 3442 } 3443 colorC[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; 3444 return color; 3445 } 3446 3447 void DistantLightSoftware::Prepare() { 3448 const double radPerDeg = M_PI / 180.0; 3449 mVectorToLight.x = cos(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); 3450 mVectorToLight.y = sin(mAzimuth * radPerDeg) * cos(mElevation * radPerDeg); 3451 mVectorToLight.z = sin(mElevation * radPerDeg); 3452 } 3453 3454 Point3D DistantLightSoftware::GetVectorToLight(const Point3D& aTargetPoint) { 3455 return mVectorToLight; 3456 } 3457 3458 uint32_t DistantLightSoftware::GetColor(uint32_t aLightColor, 3459 const Point3D& aVectorToLight) { 3460 return aLightColor; 3461 } 3462 3463 template <typename CoordType> 3464 static Point3D GenerateNormal(const uint8_t* data, int32_t stride, 3465 uint8_t* boundsBegin, uint8_t* boundsEnd, 3466 int32_t x, int32_t y, float surfaceScale, 3467 CoordType dx, CoordType dy) { 3468 const uint8_t* index = data + y * stride + x; 3469 3470 CoordType zero = 0; 3471 3472 // See this for source of constants: 3473 // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement 3474 int16_t normalX = -1 * ColorComponentAtPoint(index, stride, boundsBegin, 3475 boundsEnd, -dx, -dy, 1, 0) + 3476 1 * ColorComponentAtPoint(index, stride, boundsBegin, 3477 boundsEnd, dx, -dy, 1, 0) + 3478 -2 * ColorComponentAtPoint(index, stride, boundsBegin, 3479 boundsEnd, -dx, zero, 1, 0) + 3480 2 * ColorComponentAtPoint(index, stride, boundsBegin, 3481 boundsEnd, dx, zero, 1, 0) + 3482 -1 * ColorComponentAtPoint(index, stride, boundsBegin, 3483 boundsEnd, -dx, dy, 1, 0) + 3484 1 * ColorComponentAtPoint(index, stride, boundsBegin, 3485 boundsEnd, dx, dy, 1, 0); 3486 3487 int16_t normalY = -1 * ColorComponentAtPoint(index, stride, boundsBegin, 3488 boundsEnd, -dx, -dy, 1, 0) + 3489 -2 * ColorComponentAtPoint(index, stride, boundsBegin, 3490 boundsEnd, zero, -dy, 1, 0) + 3491 -1 * ColorComponentAtPoint(index, stride, boundsBegin, 3492 boundsEnd, dx, -dy, 1, 0) + 3493 1 * ColorComponentAtPoint(index, stride, boundsBegin, 3494 boundsEnd, -dx, dy, 1, 0) + 3495 2 * ColorComponentAtPoint(index, stride, boundsBegin, 3496 boundsEnd, zero, dy, 1, 0) + 3497 1 * ColorComponentAtPoint(index, stride, boundsBegin, 3498 boundsEnd, dx, dy, 1, 0); 3499 3500 Point3D normal; 3501 normal.x = -surfaceScale * normalX / 4.0f; 3502 normal.y = -surfaceScale * normalY / 4.0f; 3503 normal.z = 255; 3504 return Normalized(normal); 3505 } 3506 3507 template <typename LightType, typename LightingType> 3508 already_AddRefed<DataSourceSurface> 3509 FilterNodeLightingSoftware<LightType, LightingType>::Render( 3510 const IntRect& aRect) { 3511 if (mKernelUnitLength.width == floor(mKernelUnitLength.width) && 3512 mKernelUnitLength.height == floor(mKernelUnitLength.height)) { 3513 return DoRender(aRect, (int32_t)mKernelUnitLength.width, 3514 (int32_t)mKernelUnitLength.height); 3515 } 3516 return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height); 3517 } 3518 3519 template <typename LightType, typename LightingType> 3520 void FilterNodeLightingSoftware< 3521 LightType, LightingType>::RequestFromInputsForRect(const IntRect& aRect) { 3522 IntRect srcRect = aRect; 3523 srcRect.Inflate(ceil(mKernelUnitLength.width), 3524 ceil(mKernelUnitLength.height)); 3525 RequestInputRect(IN_LIGHTING_IN, srcRect); 3526 } 3527 3528 template <typename LightType, typename LightingType> 3529 IntRect FilterNodeLightingSoftware<LightType, LightingType>::MapRectToSource( 3530 const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { 3531 IntRect srcRect = aRect; 3532 srcRect.Inflate(ceil(mKernelUnitLength.width), 3533 ceil(mKernelUnitLength.height)); 3534 return MapInputRectToSource(IN_LIGHTING_IN, srcRect, aMax, aSourceNode); 3535 } 3536 3537 template <typename LightType, typename LightingType> 3538 template <typename CoordType> 3539 already_AddRefed<DataSourceSurface> 3540 FilterNodeLightingSoftware<LightType, LightingType>::DoRender( 3541 const IntRect& aRect, CoordType aKernelUnitLengthX, 3542 CoordType aKernelUnitLengthY) { 3543 MOZ_ASSERT(aKernelUnitLengthX > 0, 3544 "aKernelUnitLengthX can be a negative or zero value"); 3545 MOZ_ASSERT(aKernelUnitLengthY > 0, 3546 "aKernelUnitLengthY can be a negative or zero value"); 3547 3548 IntRect srcRect = aRect; 3549 IntSize size = aRect.Size(); 3550 srcRect.Inflate(ceil(float(aKernelUnitLengthX)), 3551 ceil(float(aKernelUnitLengthY))); 3552 3553 // Inflate the source rect by another pixel because the bilinear filtering in 3554 // ColorComponentAtPoint may want to access the margins. 3555 srcRect.Inflate(1); 3556 3557 IntRect srcRectInRenderRect = srcRect.Intersect(mRenderRect); 3558 3559 RefPtr<DataSourceSurface> input = 3560 GetInputDataSourceSurface(IN_LIGHTING_IN, srcRect, CAN_HANDLE_A8, 3561 EDGE_MODE_DUPLICATE, &srcRectInRenderRect); 3562 3563 if (!input) { 3564 return nullptr; 3565 } 3566 3567 if (input->GetFormat() != SurfaceFormat::A8) { 3568 input = FilterProcessing::ExtractAlpha(input); 3569 } 3570 3571 RefPtr<DataSourceSurface> target = 3572 Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8); 3573 if (MOZ2D_WARN_IF(!target)) { 3574 return nullptr; 3575 } 3576 3577 IntPoint offset = aRect.TopLeft() - srcRect.TopLeft(); 3578 3579 DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ); 3580 DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE); 3581 if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) { 3582 return nullptr; 3583 } 3584 3585 uint8_t* sourceData = 3586 DataAtOffset(input, sourceMap.GetMappedSurface(), offset); 3587 int32_t sourceStride = sourceMap.GetStride(); 3588 uint8_t* sourceBegin = sourceMap.GetData(); 3589 uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height; 3590 uint8_t* targetData = targetMap.GetData(); 3591 int32_t targetStride = targetMap.GetStride(); 3592 3593 uint32_t lightColor = ColorToBGRA(mColor); 3594 mLight.Prepare(); 3595 mLighting.Prepare(); 3596 3597 for (int32_t y = 0; y < size.height; y++) { 3598 for (int32_t x = 0; x < size.width; x++) { 3599 int32_t sourceIndex = y * sourceStride + x; 3600 int32_t targetIndex = y * targetStride + 4 * x; 3601 3602 Point3D normal = 3603 GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd, x, y, 3604 mSurfaceScale, aKernelUnitLengthX, aKernelUnitLengthY); 3605 3606 IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y); 3607 Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f; 3608 Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z); 3609 Point3D rayDir = mLight.GetVectorToLight(pt); 3610 uint32_t color = mLight.GetColor(lightColor, rayDir); 3611 3612 *(uint32_t*)(targetData + targetIndex) = 3613 mLighting.LightPixel(normal, rayDir, color); 3614 } 3615 3616 // Zero padding to keep valgrind happy. 3617 PodZero(&targetData[y * targetStride + 4 * size.width], 3618 targetStride - 4 * size.width); 3619 } 3620 3621 return target.forget(); 3622 } 3623 3624 DiffuseLightingSoftware::DiffuseLightingSoftware() : mDiffuseConstant(0) {} 3625 3626 bool DiffuseLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) { 3627 switch (aIndex) { 3628 case ATT_DIFFUSE_LIGHTING_DIFFUSE_CONSTANT: 3629 mDiffuseConstant = aValue; 3630 break; 3631 default: 3632 return false; 3633 } 3634 return true; 3635 } 3636 3637 uint32_t DiffuseLightingSoftware::LightPixel(const Point3D& aNormal, 3638 const Point3D& aVectorToLight, 3639 uint32_t aColor) { 3640 Float dotNL = std::max(0.0f, aNormal.DotProduct(aVectorToLight)); 3641 Float diffuseNL = mDiffuseConstant * dotNL; 3642 3643 union { 3644 uint32_t bgra; 3645 uint8_t components[4]; 3646 } color = {aColor}; 3647 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = umin( 3648 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]), 3649 255U); 3650 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = umin( 3651 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]), 3652 255U); 3653 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = umin( 3654 uint32_t(diffuseNL * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]), 3655 255U); 3656 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 255; 3657 return color.bgra; 3658 } 3659 3660 SpecularLightingSoftware::SpecularLightingSoftware() 3661 : mSpecularConstant(0), mSpecularExponent(0), mSpecularConstantInt(0) {} 3662 3663 bool SpecularLightingSoftware::SetAttribute(uint32_t aIndex, Float aValue) { 3664 switch (aIndex) { 3665 case ATT_SPECULAR_LIGHTING_SPECULAR_CONSTANT: 3666 mSpecularConstant = std::clamp(aValue, 0.0f, 255.0f); 3667 break; 3668 case ATT_SPECULAR_LIGHTING_SPECULAR_EXPONENT: 3669 mSpecularExponent = std::clamp(aValue, 1.0f, 128.0f); 3670 break; 3671 default: 3672 return false; 3673 } 3674 return true; 3675 } 3676 3677 void SpecularLightingSoftware::Prepare() { 3678 mPowCache.CacheForExponent(mSpecularExponent); 3679 mSpecularConstantInt = uint32_t(mSpecularConstant * (1 << 8)); 3680 } 3681 3682 uint32_t SpecularLightingSoftware::LightPixel(const Point3D& aNormal, 3683 const Point3D& aVectorToLight, 3684 uint32_t aColor) { 3685 Point3D vectorToEye(0, 0, 1); 3686 Point3D halfwayVector = aVectorToLight + vectorToEye; 3687 Float halfwayLength = halfwayVector.Length(); 3688 if (halfwayLength > 0) { 3689 halfwayVector /= halfwayLength; 3690 } 3691 Float dotNH = aNormal.DotProduct(halfwayVector); 3692 uint16_t dotNHi = 3693 uint16_t(dotNH * (dotNH >= 0) * (1 << PowCache::sInputIntPrecisionBits)); 3694 // The exponent for specular is in [1,128] range, so we don't need to check 3695 // and optimize for the "default power table" scenario here. 3696 MOZ_ASSERT(mPowCache.HasPowerTable()); 3697 uint32_t specularNHi = 3698 uint32_t(mSpecularConstantInt) * mPowCache.Pow(dotNHi) >> 8; 3699 3700 union { 3701 uint32_t bgra; 3702 uint8_t components[4]; 3703 } color = {aColor}; 3704 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B] = 3705 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B]) >> 3706 PowCache::sOutputIntPrecisionBits, 3707 255U); 3708 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G] = 3709 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G]) >> 3710 PowCache::sOutputIntPrecisionBits, 3711 255U); 3712 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R] = 3713 umin((specularNHi * color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R]) >> 3714 PowCache::sOutputIntPrecisionBits, 3715 255U); 3716 3717 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_A] = 3718 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_B], 3719 umax(color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_G], 3720 color.components[B8G8R8A8_COMPONENT_BYTEOFFSET_R])); 3721 return color.bgra; 3722 } 3723 3724 } // namespace gfx 3725 } // namespace mozilla