FilterNodeWebgl.cpp (36310B)
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 "FilterNodeWebgl.h" 8 9 #include <limits> 10 11 #include "DrawTargetWebglInternal.h" 12 #include "SourceSurfaceWebgl.h" 13 #include "mozilla/PodOperations.h" 14 #include "mozilla/gfx/Blur.h" 15 #include "mozilla/gfx/DrawTargetSkia.h" 16 #include "mozilla/gfx/FilterNodeSoftware.h" 17 #include "mozilla/gfx/Helpers.h" 18 #include "mozilla/gfx/Logging.h" 19 20 namespace mozilla::gfx { 21 22 FilterNodeWebgl::FilterNodeWebgl(FilterType aType) 23 : mType(aType), 24 mSoftwareFilter( 25 FilterNodeSoftware::Create(aType).downcast<FilterNodeSoftware>()) {} 26 27 FilterNodeWebgl::~FilterNodeWebgl() = default; 28 29 already_AddRefed<FilterNodeWebgl> FilterNodeWebgl::Create(FilterType aType) { 30 RefPtr<FilterNodeWebgl> filter; 31 switch (aType) { 32 case FilterType::CROP: 33 filter = new FilterNodeCropWebgl; 34 break; 35 case FilterType::TRANSFORM: 36 filter = new FilterNodeTransformWebgl; 37 break; 38 case FilterType::GAUSSIAN_BLUR: 39 filter = new FilterNodeGaussianBlurWebgl; 40 break; 41 case FilterType::PREMULTIPLY: 42 filter = new FilterNodePremultiplyWebgl; 43 break; 44 case FilterType::UNPREMULTIPLY: 45 filter = new FilterNodeUnpremultiplyWebgl; 46 break; 47 case FilterType::COLOR_MATRIX: 48 filter = new FilterNodeColorMatrixWebgl; 49 break; 50 case FilterType::LINEAR_TRANSFER: 51 filter = new FilterNodeLinearTransferWebgl; 52 break; 53 case FilterType::TABLE_TRANSFER: 54 filter = new FilterNodeTableTransferWebgl; 55 break; 56 case FilterType::OPACITY: 57 filter = new FilterNodeOpacityWebgl; 58 break; 59 default: 60 filter = new FilterNodeWebgl(aType); 61 break; 62 } 63 return filter.forget(); 64 } 65 66 int32_t FilterNodeWebgl::InputIndex(uint32_t aInputEnumIndex) const { 67 if (mSoftwareFilter) { 68 return mSoftwareFilter->InputIndex(aInputEnumIndex); 69 } 70 return -1; 71 } 72 73 bool FilterNodeWebgl::ReserveInputIndex(uint32_t aIndex) { 74 size_t inputIndex = aIndex; 75 if (std::numeric_limits<size_t>::max() - inputIndex < 1) { 76 return false; 77 } 78 if (mInputSurfaces.size() <= inputIndex) { 79 mInputSurfaces.resize(inputIndex + 1); 80 } 81 if (mInputFilters.size() <= inputIndex) { 82 mInputFilters.resize(inputIndex + 1); 83 } 84 return true; 85 } 86 87 bool FilterNodeWebgl::SetInputAccel(uint32_t aIndex, SourceSurface* aSurface) { 88 if (ReserveInputIndex(aIndex)) { 89 mInputSurfaces[aIndex] = aSurface; 90 mInputFilters[aIndex] = nullptr; 91 return true; 92 } 93 return false; 94 } 95 96 bool FilterNodeWebgl::SetInputSoftware(uint32_t aIndex, 97 SourceSurface* aSurface) { 98 if (mSoftwareFilter) { 99 mSoftwareFilter->SetInput(aIndex, aSurface); 100 } 101 mInputMask |= (1 << aIndex); 102 return true; 103 } 104 105 void FilterNodeWebgl::SetInput(uint32_t aIndex, SourceSurface* aSurface) { 106 int32_t inputIndex = InputIndex(aIndex); 107 if (inputIndex < 0 || !SetInputAccel(inputIndex, aSurface) || 108 !SetInputSoftware(inputIndex, aSurface)) { 109 gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex; 110 return; 111 } 112 } 113 114 void FilterNodeWebgl::SetInput(uint32_t aIndex, FilterNode* aFilter) { 115 if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_WEBGL) { 116 MOZ_ASSERT(false, "FilterNodeWebgl required as input"); 117 return; 118 } 119 120 int32_t inputIndex = InputIndex(aIndex); 121 if (inputIndex < 0 || !ReserveInputIndex(inputIndex)) { 122 gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex; 123 return; 124 } 125 126 auto* webglFilter = static_cast<FilterNodeWebgl*>(aFilter); 127 mInputFilters[inputIndex] = webglFilter; 128 mInputSurfaces[inputIndex] = nullptr; 129 if (mSoftwareFilter) { 130 MOZ_ASSERT(!webglFilter || webglFilter->mSoftwareFilter); 131 mSoftwareFilter->SetInput( 132 aIndex, webglFilter ? webglFilter->mSoftwareFilter.get() : nullptr); 133 } 134 } 135 136 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, bool aValue) { 137 if (mSoftwareFilter) { 138 mSoftwareFilter->SetAttribute(aIndex, aValue); 139 } 140 } 141 142 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) { 143 if (mSoftwareFilter) { 144 mSoftwareFilter->SetAttribute(aIndex, aValue); 145 } 146 } 147 148 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, Float aValue) { 149 if (mSoftwareFilter) { 150 mSoftwareFilter->SetAttribute(aIndex, aValue); 151 } 152 } 153 154 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Size& aValue) { 155 if (mSoftwareFilter) { 156 mSoftwareFilter->SetAttribute(aIndex, aValue); 157 } 158 } 159 160 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntSize& aValue) { 161 if (mSoftwareFilter) { 162 mSoftwareFilter->SetAttribute(aIndex, aValue); 163 } 164 } 165 166 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntPoint& aValue) { 167 if (mSoftwareFilter) { 168 mSoftwareFilter->SetAttribute(aIndex, aValue); 169 } 170 } 171 172 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) { 173 if (mSoftwareFilter) { 174 mSoftwareFilter->SetAttribute(aIndex, aValue); 175 } 176 } 177 178 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntRect& aValue) { 179 if (mSoftwareFilter) { 180 mSoftwareFilter->SetAttribute(aIndex, aValue); 181 } 182 } 183 184 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point& aValue) { 185 if (mSoftwareFilter) { 186 mSoftwareFilter->SetAttribute(aIndex, aValue); 187 } 188 } 189 190 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix& aValue) { 191 if (mSoftwareFilter) { 192 mSoftwareFilter->SetAttribute(aIndex, aValue); 193 } 194 } 195 196 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) { 197 if (mSoftwareFilter) { 198 mSoftwareFilter->SetAttribute(aIndex, aValue); 199 } 200 } 201 202 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point3D& aValue) { 203 if (mSoftwareFilter) { 204 mSoftwareFilter->SetAttribute(aIndex, aValue); 205 } 206 } 207 208 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const DeviceColor& aValue) { 209 if (mSoftwareFilter) { 210 mSoftwareFilter->SetAttribute(aIndex, aValue); 211 } 212 } 213 214 void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Float* aValues, 215 uint32_t aSize) { 216 if (mSoftwareFilter) { 217 mSoftwareFilter->SetAttribute(aIndex, aValues, aSize); 218 } 219 } 220 221 IntRect FilterNodeWebgl::MapRectToSource(const IntRect& aRect, 222 const IntRect& aMax, 223 FilterNode* aSourceNode) { 224 if (mSoftwareFilter) { 225 if (aSourceNode && aSourceNode->GetBackendType() == FILTER_BACKEND_WEBGL) { 226 aSourceNode = static_cast<FilterNodeWebgl*>(aSourceNode)->mSoftwareFilter; 227 } 228 return mSoftwareFilter->MapRectToSource(aRect, aMax, aSourceNode); 229 } 230 return aMax; 231 } 232 233 void FilterNodeWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, 234 const Point& aDestPoint, const DrawOptions& aOptions, 235 FilterNodeWebgl* aParent) { 236 ResolveAllInputs(aDT, aParent); 237 238 MOZ_ASSERT(mSoftwareFilter); 239 aDT->DrawFilterFallback(mSoftwareFilter, aSourceRect, aDestPoint, aOptions); 240 } 241 242 already_AddRefed<SourceSurface> FilterNodeWebgl::DrawChild( 243 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 244 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 245 ResolveAllInputs(aDT, aParent); 246 247 MOZ_ASSERT(mSoftwareFilter); 248 RefPtr<DrawTarget> swDT = aDT->mSkia->CreateSimilarDrawTarget( 249 IntSize::Ceil(aSourceRect.Size()), aDT->GetFormat()); 250 if (!swDT) { 251 return nullptr; 252 } 253 swDT->DrawFilter(mSoftwareFilter, aSourceRect, Point(0, 0), aOptions); 254 aSurfaceOffset = aSourceRect.TopLeft(); 255 aColor = DeviceColor(1, 1, 1, 1); 256 return swDT->Snapshot(); 257 } 258 259 IntRect FilterNodeWebgl::MapInputRectToSource(uint32_t aInputEnumIndex, 260 const IntRect& aRect, 261 const IntRect& aMax, 262 FilterNode* aSourceNode) { 263 int32_t inputIndex = InputIndex(aInputEnumIndex); 264 if (inputIndex < 0) { 265 gfxDevCrash(LogReason::FilterInputError) 266 << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs(); 267 return aMax; 268 } 269 if ((uint32_t)inputIndex < NumberOfSetInputs()) { 270 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIndex]) { 271 return filter->MapRectToSource(aRect, aMax, aSourceNode); 272 } 273 } 274 if (this == aSourceNode) { 275 return aRect; 276 } 277 return IntRect(); 278 } 279 280 void FilterNodeWebgl::ResolveAllInputs(DrawTargetWebgl* aDT, 281 FilterNodeWebgl* aParent) { 282 ResolveInputs(aDT, false, aParent); 283 for (const auto& filter : mInputFilters) { 284 if (filter) { 285 filter->ResolveAllInputs(aDT, this); 286 } 287 } 288 } 289 290 int32_t FilterNodeCropWebgl::InputIndex(uint32_t aInputEnumIndex) const { 291 switch (aInputEnumIndex) { 292 case IN_CROP_IN: 293 return 0; 294 default: 295 return -1; 296 } 297 } 298 299 void FilterNodeCropWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) { 300 MOZ_ASSERT(aIndex == ATT_CROP_RECT); 301 Rect srcRect = aValue; 302 srcRect.Round(); 303 if (!srcRect.ToIntRect(&mCropRect)) { 304 mCropRect = IntRect(); 305 } 306 FilterNodeWebgl::SetAttribute(aIndex, aValue); 307 } 308 309 IntRect FilterNodeCropWebgl::MapRectToSource(const IntRect& aRect, 310 const IntRect& aMax, 311 FilterNode* aSourceNode) { 312 return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax, 313 aSourceNode); 314 } 315 316 void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, 317 const Point& aDestPoint, 318 const DrawOptions& aOptions, 319 FilterNodeWebgl* aParent) { 320 ResolveInputs(aDT, true, aParent); 321 322 uint32_t inputIdx = InputIndex(IN_CROP_IN); 323 if (inputIdx < NumberOfSetInputs()) { 324 Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); 325 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 326 filter->Draw(aDT, croppedSource, 327 aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(), 328 aOptions, this); 329 } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 330 aDT->DrawSurface(surface, 331 croppedSource - aSourceRect.TopLeft() + aDestPoint, 332 croppedSource, DrawSurfaceOptions(), aOptions); 333 } 334 } 335 } 336 337 bool FilterNodeCropWebgl::DrawAccel(DrawTargetWebgl* aDT, 338 const Rect& aSourceRect, 339 const Point& aDestPoint, 340 const DrawOptions& aOptions, 341 FilterNodeWebgl* aParent) { 342 uint32_t inputIdx = InputIndex(IN_CROP_IN); 343 if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { 344 Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); 345 FilterNodeWebgl* filter = mInputFilters[inputIdx]; 346 switch (filter->GetType()) { 347 case FilterType::COLOR_MATRIX: 348 case FilterType::LINEAR_TRANSFER: 349 case FilterType::TABLE_TRANSFER: 350 // Crop filters are sometimes generated before evaluating a color matrix 351 // filter. 352 return filter->DrawAccel( 353 aDT, croppedSource, 354 aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(), 355 aOptions, this); 356 default: 357 break; 358 } 359 } 360 return false; 361 } 362 363 already_AddRefed<SourceSurface> FilterNodeCropWebgl::DrawChild( 364 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 365 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 366 ResolveInputs(aDT, true, aParent); 367 368 uint32_t inputIdx = InputIndex(IN_CROP_IN); 369 if (inputIdx < NumberOfSetInputs()) { 370 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 371 Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); 372 return filter->DrawChild(this, aDT, croppedSource, aOptions, 373 aSurfaceOffset, aColor); 374 } 375 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 376 aSurfaceOffset, aColor); 377 } 378 return nullptr; 379 } 380 381 int32_t FilterNodeTransformWebgl::InputIndex(uint32_t aInputEnumIndex) const { 382 switch (aInputEnumIndex) { 383 case IN_TRANSFORM_IN: 384 return 0; 385 default: 386 return -1; 387 } 388 } 389 390 void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) { 391 MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); 392 mSamplingFilter = static_cast<SamplingFilter>(aValue); 393 FilterNodeWebgl::SetAttribute(aIndex, aValue); 394 } 395 396 void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex, 397 const Matrix& aValue) { 398 MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); 399 mMatrix = aValue; 400 FilterNodeWebgl::SetAttribute(aIndex, aValue); 401 } 402 403 IntRect FilterNodeTransformWebgl::MapRectToSource(const IntRect& aRect, 404 const IntRect& aMax, 405 FilterNode* aSourceNode) { 406 if (aRect.IsEmpty()) { 407 return IntRect(); 408 } 409 Matrix inv(mMatrix); 410 if (!inv.Invert()) { 411 return aMax; 412 } 413 Rect rect = inv.TransformBounds(Rect(aRect)); 414 rect.RoundOut(); 415 IntRect intRect; 416 if (!rect.ToIntRect(&intRect)) { 417 return aMax; 418 } 419 return MapInputRectToSource(IN_TRANSFORM_IN, intRect, aMax, aSourceNode); 420 } 421 422 void FilterNodeTransformWebgl::Draw(DrawTargetWebgl* aDT, 423 const Rect& aSourceRect, 424 const Point& aDestPoint, 425 const DrawOptions& aOptions, 426 FilterNodeWebgl* aParent) { 427 if (!mMatrix.IsTranslation()) { 428 FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); 429 return; 430 } 431 432 ResolveInputs(aDT, true, aParent); 433 434 uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); 435 if (inputIdx < NumberOfSetInputs()) { 436 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 437 filter->Draw(aDT, aSourceRect - mMatrix.GetTranslation(), aDestPoint, 438 aOptions, aParent); 439 } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 440 aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()), 441 aSourceRect - mMatrix.GetTranslation(), 442 DrawSurfaceOptions(mSamplingFilter), aOptions); 443 } 444 } 445 } 446 447 already_AddRefed<SourceSurface> FilterNodeTransformWebgl::DrawChild( 448 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 449 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 450 if (!mMatrix.IsIntegerTranslation()) { 451 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 452 aSurfaceOffset, aColor); 453 } 454 455 ResolveInputs(aDT, true, aParent); 456 457 uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); 458 if (inputIdx < NumberOfSetInputs()) { 459 if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 460 aSurfaceOffset = mMatrix.GetTranslation().Round(); 461 aColor = DeviceColor(1, 1, 1, aOptions.mAlpha); 462 return surface.forget(); 463 } 464 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 465 aSurfaceOffset, aColor); 466 } 467 return nullptr; 468 } 469 470 FilterNodeDeferInputWebgl::FilterNodeDeferInputWebgl( 471 RefPtr<Path> aPath, const Pattern& aPattern, const IntRect& aSourceRect, 472 const Matrix& aDestTransform, const DrawOptions& aOptions, 473 const StrokeOptions* aStrokeOptions) 474 : mPath(std::move(aPath)), 475 mSourceRect(aSourceRect), 476 mDestTransform(aDestTransform), 477 mOptions(aOptions) { 478 mPattern.Init(aPattern); 479 if (aStrokeOptions) { 480 mStrokeOptions = Some(*aStrokeOptions); 481 if (aStrokeOptions->mDashLength > 0) { 482 mDashPatternStorage.reset(new Float[aStrokeOptions->mDashLength]); 483 PodCopy(mDashPatternStorage.get(), aStrokeOptions->mDashPattern, 484 aStrokeOptions->mDashLength); 485 mStrokeOptions->mDashPattern = mDashPatternStorage.get(); 486 } 487 } 488 SetAttribute(ATT_TRANSFORM_MATRIX, 489 Matrix::Translation(mSourceRect.TopLeft())); 490 } 491 492 void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT, bool aAccel, 493 FilterNodeWebgl* aParent) { 494 uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); 495 bool hasAccel = false; 496 if (inputIdx < NumberOfSetInputs() && mInputSurfaces[inputIdx]) { 497 if (aAccel || (mInputMask & (1 << inputIdx))) { 498 return; 499 } 500 hasAccel = true; 501 } 502 RefPtr<SourceSurface> surface; 503 SurfaceFormat format = SurfaceFormat::B8G8R8A8; 504 static const ColorPattern maskPattern(DeviceColor(1, 1, 1, 1)); 505 const Pattern* pattern = mPattern.GetPattern(); 506 if (aAccel) { 507 // If using acceleration on a color pattern, attempt to blur solely on the 508 // alpha values to significantly reduce data churn, as the color will only 509 // vary linearly with alpha over the input surface. The color will be 510 // incorporated on the final mask draw. 511 if (mPattern.GetPattern()->GetType() == PatternType::COLOR) { 512 format = SurfaceFormat::A8; 513 pattern = &maskPattern; 514 } 515 surface = aDT->ResolveFilterInputAccel( 516 mPath, *pattern, mSourceRect, mDestTransform, mOptions, 517 mStrokeOptions.ptrOr(nullptr), format); 518 } 519 if (!surface) { 520 surface = aDT->mSkia->ResolveFilterInput( 521 mPath, *pattern, mSourceRect, mDestTransform, mOptions, 522 mStrokeOptions.ptrOr(nullptr), format); 523 } 524 if (hasAccel) { 525 SetInputSoftware(inputIdx, surface); 526 } else if (surface && surface->GetFormat() == SurfaceFormat::A8) { 527 SetInputAccel(inputIdx, surface); 528 } else { 529 SetInput(inputIdx, surface); 530 } 531 } 532 533 void FilterNodeDeferInputWebgl::Draw(DrawTargetWebgl* aDT, 534 const Rect& aSourceRect, 535 const Point& aDestPoint, 536 const DrawOptions& aOptions, 537 FilterNodeWebgl* aParent) { 538 const Pattern* pattern = mPattern.GetPattern(); 539 AutoRestoreTransform restore(aDT); 540 aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); 541 aDT->ConcatTransform( 542 Matrix(mDestTransform).PostTranslate(aDestPoint - aSourceRect.TopLeft())); 543 DrawOptions options(aOptions.mAlpha * mOptions.mAlpha, 544 aOptions.mCompositionOp, mOptions.mAntialiasMode); 545 if (mStrokeOptions) { 546 aDT->Stroke(mPath, *pattern, *mStrokeOptions, options); 547 } else { 548 aDT->Fill(mPath, *pattern, options); 549 } 550 aDT->PopClip(); 551 } 552 553 already_AddRefed<SourceSurface> FilterNodeDeferInputWebgl::DrawChild( 554 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 555 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 556 ResolveInputs(aDT, true, aParent); 557 558 uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); 559 if (inputIdx < NumberOfSetInputs()) { 560 if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 561 aSurfaceOffset = mMatrix.GetTranslation().Round(); 562 // If the output will be a mask, then supply the color that should be 563 // rendered with it. 564 aColor = 565 mPattern.GetPattern()->GetType() == PatternType::COLOR 566 ? static_cast<const ColorPattern*>(mPattern.GetPattern())->mColor 567 : DeviceColor(1, 1, 1, 1); 568 aColor.a *= aOptions.mAlpha; 569 return surface.forget(); 570 } 571 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 572 aSurfaceOffset, aColor); 573 } 574 return nullptr; 575 } 576 577 int32_t FilterNodeGaussianBlurWebgl::InputIndex( 578 uint32_t aInputEnumIndex) const { 579 switch (aInputEnumIndex) { 580 case IN_GAUSSIAN_BLUR_IN: 581 return 0; 582 default: 583 return -1; 584 } 585 } 586 587 void FilterNodeGaussianBlurWebgl::SetAttribute(uint32_t aIndex, float aValue) { 588 MOZ_ASSERT(aIndex == ATT_GAUSSIAN_BLUR_STD_DEVIATION); 589 // Match the FilterNodeSoftware blur limit. 590 mStdDeviation = std::clamp(aValue, 0.0f, 100.0f); 591 FilterNodeWebgl::SetAttribute(aIndex, aValue); 592 } 593 594 IntRect FilterNodeGaussianBlurWebgl::MapRectToSource(const IntRect& aRect, 595 const IntRect& aMax, 596 FilterNode* aSourceNode) { 597 return MapInputRectToSource(IN_GAUSSIAN_BLUR_IN, aRect, aMax, aSourceNode); 598 } 599 600 void FilterNodeGaussianBlurWebgl::Draw(DrawTargetWebgl* aDT, 601 const Rect& aSourceRect, 602 const Point& aDestPoint, 603 const DrawOptions& aOptions, 604 FilterNodeWebgl* aParent) { 605 ResolveInputs(aDT, true, aParent); 606 607 uint32_t inputIdx = InputIndex(IN_GAUSSIAN_BLUR_IN); 608 if (inputIdx < NumberOfSetInputs()) { 609 bool success = false; 610 Point surfaceOffset; 611 DeviceColor color(1, 1, 1, 1); 612 if (RefPtr<SourceSurface> surface = 613 mInputFilters[inputIdx] ? mInputFilters[inputIdx]->DrawChild( 614 this, aDT, aSourceRect, DrawOptions(), 615 surfaceOffset, color) 616 : mInputSurfaces[inputIdx]) { 617 aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); 618 IntRect surfRect = RoundedOut( 619 Rect(surface->GetRect()).Intersect(aSourceRect - surfaceOffset)); 620 Point destOffset = aDestPoint + Point(surfRect.TopLeft()) + 621 surfaceOffset - aSourceRect.TopLeft(); 622 success = surfRect.IsEmpty() || 623 aDT->BlurSurface(mStdDeviation, surface, surfRect, destOffset, 624 aOptions, color); 625 aDT->PopClip(); 626 } 627 if (!success) { 628 FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); 629 } 630 } 631 } 632 633 void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex, 634 const Matrix5x4& aValue) { 635 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); 636 mMatrix = aValue; 637 FilterNodeWebgl::SetAttribute(aIndex, aValue); 638 } 639 640 void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex, 641 uint32_t aValue) { 642 MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); 643 mAlphaMode = (AlphaMode)aValue; 644 FilterNodeWebgl::SetAttribute(aIndex, aValue); 645 } 646 647 int32_t FilterNodeColorMatrixWebgl::InputIndex(uint32_t aInputEnumIndex) const { 648 switch (aInputEnumIndex) { 649 case IN_COLOR_MATRIX_IN: 650 return 0; 651 default: 652 return -1; 653 } 654 } 655 656 static bool DrawColorMatrixFilter(DrawTargetWebgl* aDT, const Point& aDestPoint, 657 const DrawOptions& aOptions, 658 const RefPtr<SourceSurface>& aSurface, 659 const Rect& aSourceRect, 660 const Point& aSurfaceOffset, 661 const Matrix5x4& aMatrix, 662 const DeviceColor& aColor) { 663 IntRect surfRect = RoundedOut( 664 Rect(aSurface->GetRect()).Intersect(aSourceRect - aSurfaceOffset)); 665 if (surfRect.IsEmpty()) { 666 return true; 667 } 668 aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); 669 Point destOffset = aDestPoint + Point(surfRect.TopLeft()) + aSurfaceOffset - 670 aSourceRect.TopLeft(); 671 bool success = true; 672 if (aSurface->GetFormat() == SurfaceFormat::A8) { 673 // Mask surfaces only use a solid color that is supplied outside the 674 // surface. This color can be transformed without requiring a shader. 675 Point4D outColor = 676 Matrix4x4(aMatrix.components) 677 .TransformPoint(Point4D(aColor.r, aColor.g, aColor.b, aColor.a)) + 678 Point4D(aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54); 679 SurfacePattern maskPattern(aSurface, ExtendMode::CLAMP, 680 Matrix::Translation(destOffset)); 681 if (!surfRect.IsEqualEdges(aSurface->GetRect())) { 682 maskPattern.mSamplingRect = surfRect; 683 } 684 aDT->Mask(ColorPattern( 685 DeviceColor(outColor.x, outColor.y, outColor.z, outColor.w)), 686 maskPattern, aOptions); 687 } else { 688 // For normal surfaces, try to use the color matrix filter shader. 689 success = 690 aDT->FilterSurface(aMatrix, aSurface, surfRect, destOffset, aOptions); 691 } 692 aDT->PopClip(); 693 return success; 694 } 695 696 bool FilterNodeColorMatrixWebgl::DrawAccel(DrawTargetWebgl* aDT, 697 const Rect& aSourceRect, 698 const Point& aDestPoint, 699 const DrawOptions& aOptions, 700 FilterNodeWebgl* aParent) { 701 if (!aParent || mAlphaMode != ALPHA_MODE_STRAIGHT) { 702 return false; 703 } 704 switch (aParent->GetType()) { 705 case FilterType::PREMULTIPLY: 706 case FilterType::CROP: 707 break; 708 default: 709 return false; 710 } 711 712 ResolveInputs(aDT, true, aParent); 713 714 uint32_t inputIdx = InputIndex(IN_COLOR_MATRIX_IN); 715 if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { 716 FilterNodeWebgl* filter = mInputFilters[inputIdx]; 717 if (filter->GetType() == FilterType::UNPREMULTIPLY) { 718 bool success = false; 719 Point surfaceOffset; 720 DeviceColor color; 721 if (RefPtr<SourceSurface> surface = filter->DrawChild( 722 this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) { 723 success = 724 DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface, 725 aSourceRect, surfaceOffset, mMatrix, color); 726 } 727 return success; 728 } 729 } 730 return false; 731 } 732 733 void FilterNodeComponentTransferWebgl::SetAttribute(uint32_t aIndex, 734 bool aValue) { 735 switch (aIndex) { 736 case ATT_TRANSFER_DISABLE_R: 737 mDisableR = aValue; 738 break; 739 case ATT_TRANSFER_DISABLE_G: 740 mDisableG = aValue; 741 break; 742 case ATT_TRANSFER_DISABLE_B: 743 mDisableB = aValue; 744 break; 745 case ATT_TRANSFER_DISABLE_A: 746 mDisableA = aValue; 747 break; 748 default: 749 gfxDevCrash(LogReason::FilterInputError) 750 << "FilterNodeComponentTransferWebgl: Invalid attribute " << aIndex; 751 break; 752 } 753 FilterNodeWebgl::SetAttribute(aIndex, aValue); 754 } 755 756 int32_t FilterNodeComponentTransferWebgl::InputIndex( 757 uint32_t aInputEnumIndex) const { 758 switch (aInputEnumIndex) { 759 case IN_TRANSFER_IN: 760 return 0; 761 default: 762 return -1; 763 } 764 } 765 766 bool FilterNodeComponentTransferWebgl::DrawAccel(DrawTargetWebgl* aDT, 767 const Rect& aSourceRect, 768 const Point& aDestPoint, 769 const DrawOptions& aOptions, 770 FilterNodeWebgl* aParent) { 771 if (!aParent) { 772 return false; 773 } 774 switch (aParent->GetType()) { 775 case FilterType::PREMULTIPLY: 776 case FilterType::CROP: 777 break; 778 default: 779 return false; 780 } 781 782 Matrix5x4 mat5x4; 783 if (!ToColorMatrix(mat5x4)) { 784 return false; 785 } 786 787 ResolveInputs(aDT, true, aParent); 788 789 uint32_t inputIdx = InputIndex(IN_TRANSFER_IN); 790 if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { 791 FilterNodeWebgl* filter = mInputFilters[inputIdx]; 792 if (filter->GetType() == FilterType::UNPREMULTIPLY) { 793 bool success = false; 794 Point surfaceOffset; 795 DeviceColor color; 796 if (RefPtr<SourceSurface> surface = filter->DrawChild( 797 this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) { 798 success = 799 DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface, 800 aSourceRect, surfaceOffset, mat5x4, color); 801 } 802 return success; 803 } 804 } 805 return false; 806 } 807 808 void FilterNodeLinearTransferWebgl::SetAttribute(uint32_t aIndex, 809 Float aValue) { 810 switch (aIndex) { 811 case ATT_LINEAR_TRANSFER_SLOPE_R: 812 mSlope.r = aValue; 813 break; 814 case ATT_LINEAR_TRANSFER_INTERCEPT_R: 815 mIntercept.r = aValue; 816 break; 817 case ATT_LINEAR_TRANSFER_SLOPE_G: 818 mSlope.g = aValue; 819 break; 820 case ATT_LINEAR_TRANSFER_INTERCEPT_G: 821 mIntercept.g = aValue; 822 break; 823 case ATT_LINEAR_TRANSFER_SLOPE_B: 824 mSlope.b = aValue; 825 break; 826 case ATT_LINEAR_TRANSFER_INTERCEPT_B: 827 mIntercept.b = aValue; 828 break; 829 case ATT_LINEAR_TRANSFER_SLOPE_A: 830 mSlope.a = aValue; 831 break; 832 case ATT_LINEAR_TRANSFER_INTERCEPT_A: 833 mIntercept.a = aValue; 834 break; 835 default: 836 MOZ_ASSERT(false); 837 break; 838 } 839 FilterNodeWebgl::SetAttribute(aIndex, aValue); 840 } 841 842 bool FilterNodeLinearTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const { 843 // Linear filters can be interpreted as a scale and translation matrix. 844 aMatrix = Matrix5x4(); 845 if (!mDisableR) { 846 aMatrix._11 = mSlope.r; 847 aMatrix._51 = mIntercept.r; 848 } 849 if (!mDisableG) { 850 aMatrix._22 = mSlope.g; 851 aMatrix._52 = mIntercept.g; 852 } 853 if (!mDisableB) { 854 aMatrix._33 = mSlope.b; 855 aMatrix._53 = mIntercept.b; 856 } 857 if (!mDisableA) { 858 aMatrix._44 = mSlope.a; 859 aMatrix._54 = mIntercept.a; 860 } 861 return true; 862 } 863 864 void FilterNodeTableTransferWebgl::SetAttribute(uint32_t aIndex, 865 const Float* aValues, 866 uint32_t aSize) { 867 std::vector<Float> table(aValues, aValues + aSize); 868 switch (aIndex) { 869 case ATT_TABLE_TRANSFER_TABLE_R: 870 mTableR = table; 871 break; 872 case ATT_TABLE_TRANSFER_TABLE_G: 873 mTableG = table; 874 break; 875 case ATT_TABLE_TRANSFER_TABLE_B: 876 mTableB = table; 877 break; 878 case ATT_TABLE_TRANSFER_TABLE_A: 879 mTableA = table; 880 break; 881 default: 882 MOZ_ASSERT(false); 883 break; 884 } 885 FilterNodeWebgl::SetAttribute(aIndex, aValues, aSize); 886 } 887 888 bool FilterNodeTableTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const { 889 // 2 element table transfers are effectively linear transfers. These can be 890 // interpreted as a scale and translation matrix. 891 if ((mDisableR || mTableR.size() == 2) && 892 (mDisableG || mTableG.size() == 2) && 893 (mDisableB || mTableB.size() == 2) && 894 (mDisableA || mTableA.size() == 2)) { 895 aMatrix = Matrix5x4(); 896 if (!mDisableR) { 897 aMatrix._11 = mTableR[1] - mTableR[0]; 898 aMatrix._51 = mTableR[0]; 899 } 900 if (!mDisableG) { 901 aMatrix._22 = mTableG[1] - mTableG[0]; 902 aMatrix._52 = mTableG[0]; 903 } 904 if (!mDisableB) { 905 aMatrix._33 = mTableB[1] - mTableB[0]; 906 aMatrix._53 = mTableB[0]; 907 } 908 if (!mDisableA) { 909 aMatrix._44 = mTableA[1] - mTableA[0]; 910 aMatrix._54 = mTableA[0]; 911 } 912 return true; 913 } 914 return true; 915 } 916 917 int32_t FilterNodePremultiplyWebgl::InputIndex(uint32_t aInputEnumIndex) const { 918 switch (aInputEnumIndex) { 919 case IN_PREMULTIPLY_IN: 920 return 0; 921 default: 922 return -1; 923 } 924 } 925 926 void FilterNodePremultiplyWebgl::Draw(DrawTargetWebgl* aDT, 927 const Rect& aSourceRect, 928 const Point& aDestPoint, 929 const DrawOptions& aOptions, 930 FilterNodeWebgl* aParent) { 931 uint32_t inputIdx = InputIndex(IN_PREMULTIPLY_IN); 932 if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { 933 FilterNodeWebgl* filter = mInputFilters[inputIdx]; 934 switch (filter->GetType()) { 935 case FilterType::CROP: 936 case FilterType::COLOR_MATRIX: 937 case FilterType::LINEAR_TRANSFER: 938 case FilterType::TABLE_TRANSFER: 939 // For color matrix filters, they will normally be preceded by a premul 940 // filter. In certain cases, after the premul there is a crop before the 941 // color matrix filter is actually evaluated. Here, we use DrawAccel to 942 // only handle the filter if it can actually be accelerated, otherwise 943 // falling back to a software color matrix filter below. 944 if (filter->DrawAccel(aDT, aSourceRect, aDestPoint, aOptions, this)) { 945 return; 946 } 947 break; 948 default: 949 break; 950 } 951 } 952 FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); 953 } 954 955 int32_t FilterNodeUnpremultiplyWebgl::InputIndex( 956 uint32_t aInputEnumIndex) const { 957 switch (aInputEnumIndex) { 958 case IN_UNPREMULTIPLY_IN: 959 return 0; 960 default: 961 return -1; 962 } 963 } 964 965 already_AddRefed<SourceSurface> FilterNodeUnpremultiplyWebgl::DrawChild( 966 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 967 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 968 switch (aParent->GetType()) { 969 case FilterType::COLOR_MATRIX: 970 case FilterType::LINEAR_TRANSFER: 971 case FilterType::TABLE_TRANSFER: 972 // Unpremul should always be the child of a color matrix filter. 973 break; 974 default: 975 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 976 aSurfaceOffset, aColor); 977 } 978 979 ResolveInputs(aDT, true, aParent); 980 981 uint32_t inputIdx = InputIndex(IN_UNPREMULTIPLY_IN); 982 if (inputIdx < NumberOfSetInputs()) { 983 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 984 if (RefPtr<SourceSurface> surface = filter->DrawChild( 985 this, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor)) { 986 return surface.forget(); 987 } 988 } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 989 aColor = DeviceColor(1, 1, 1, aOptions.mAlpha); 990 return surface.forget(); 991 } 992 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 993 aSurfaceOffset, aColor); 994 } 995 return nullptr; 996 } 997 998 int32_t FilterNodeOpacityWebgl::InputIndex(uint32_t aInputEnumIndex) const { 999 switch (aInputEnumIndex) { 1000 case IN_OPACITY_IN: 1001 return 0; 1002 default: 1003 return -1; 1004 } 1005 } 1006 1007 void FilterNodeOpacityWebgl::SetAttribute(uint32_t aIndex, Float aValue) { 1008 MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE); 1009 mValue = aValue; 1010 FilterNodeWebgl::SetAttribute(aIndex, aValue); 1011 } 1012 1013 void FilterNodeOpacityWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, 1014 const Point& aDestPoint, 1015 const DrawOptions& aOptions, 1016 FilterNodeWebgl* aParent) { 1017 ResolveInputs(aDT, true, aParent); 1018 1019 uint32_t inputIdx = InputIndex(IN_OPACITY_IN); 1020 if (inputIdx < NumberOfSetInputs()) { 1021 // Opacity filters need only modify the DrawOptions alpha value. 1022 DrawOptions options(aOptions); 1023 options.mAlpha *= mValue; 1024 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 1025 filter->Draw(aDT, aSourceRect, aDestPoint, options, this); 1026 } else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) { 1027 aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()), 1028 aSourceRect, DrawSurfaceOptions(), options); 1029 } 1030 } 1031 } 1032 1033 already_AddRefed<SourceSurface> FilterNodeOpacityWebgl::DrawChild( 1034 FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, 1035 const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { 1036 ResolveInputs(aDT, true, aParent); 1037 1038 uint32_t inputIdx = InputIndex(IN_OPACITY_IN); 1039 if (inputIdx < NumberOfSetInputs()) { 1040 if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) { 1041 // Opacity filters need only modify he DrawOptions alpha value. 1042 DrawOptions options(aOptions); 1043 options.mAlpha *= mValue; 1044 return filter->DrawChild(this, aDT, aSourceRect, options, aSurfaceOffset, 1045 aColor); 1046 } 1047 return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, 1048 aSurfaceOffset, aColor); 1049 } 1050 return nullptr; 1051 } 1052 1053 } // namespace mozilla::gfx