/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FilterNodeWebgl.h" #include #include "DrawTargetWebglInternal.h" #include "SourceSurfaceWebgl.h" #include "mozilla/PodOperations.h" #include "mozilla/gfx/Blur.h" #include "mozilla/gfx/DrawTargetSkia.h" #include "mozilla/gfx/FilterNodeSoftware.h" #include "mozilla/gfx/Helpers.h" #include "mozilla/gfx/Logging.h" namespace mozilla::gfx { FilterNodeWebgl::FilterNodeWebgl(FilterType aType) : mType(aType), mSoftwareFilter( FilterNodeSoftware::Create(aType).downcast()) {} FilterNodeWebgl::~FilterNodeWebgl() = default; already_AddRefed FilterNodeWebgl::Create(FilterType aType) { RefPtr filter; switch (aType) { case FilterType::CROP: filter = new FilterNodeCropWebgl; break; case FilterType::TRANSFORM: filter = new FilterNodeTransformWebgl; break; case FilterType::GAUSSIAN_BLUR: filter = new FilterNodeGaussianBlurWebgl; break; case FilterType::PREMULTIPLY: filter = new FilterNodePremultiplyWebgl; break; case FilterType::UNPREMULTIPLY: filter = new FilterNodeUnpremultiplyWebgl; break; case FilterType::COLOR_MATRIX: filter = new FilterNodeColorMatrixWebgl; break; case FilterType::LINEAR_TRANSFER: filter = new FilterNodeLinearTransferWebgl; break; case FilterType::TABLE_TRANSFER: filter = new FilterNodeTableTransferWebgl; break; case FilterType::OPACITY: filter = new FilterNodeOpacityWebgl; break; default: filter = new FilterNodeWebgl(aType); break; } return filter.forget(); } int32_t FilterNodeWebgl::InputIndex(uint32_t aInputEnumIndex) const { if (mSoftwareFilter) { return mSoftwareFilter->InputIndex(aInputEnumIndex); } return -1; } bool FilterNodeWebgl::ReserveInputIndex(uint32_t aIndex) { size_t inputIndex = aIndex; if (std::numeric_limits::max() - inputIndex < 1) { return false; } if (mInputSurfaces.size() <= inputIndex) { mInputSurfaces.resize(inputIndex + 1); } if (mInputFilters.size() <= inputIndex) { mInputFilters.resize(inputIndex + 1); } return true; } bool FilterNodeWebgl::SetInputAccel(uint32_t aIndex, SourceSurface* aSurface) { if (ReserveInputIndex(aIndex)) { mInputSurfaces[aIndex] = aSurface; mInputFilters[aIndex] = nullptr; return true; } return false; } bool FilterNodeWebgl::SetInputSoftware(uint32_t aIndex, SourceSurface* aSurface) { if (mSoftwareFilter) { mSoftwareFilter->SetInput(aIndex, aSurface); } mInputMask |= (1 << aIndex); return true; } void FilterNodeWebgl::SetInput(uint32_t aIndex, SourceSurface* aSurface) { int32_t inputIndex = InputIndex(aIndex); if (inputIndex < 0 || !SetInputAccel(inputIndex, aSurface) || !SetInputSoftware(inputIndex, aSurface)) { gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex; return; } } void FilterNodeWebgl::SetInput(uint32_t aIndex, FilterNode* aFilter) { if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_WEBGL) { MOZ_ASSERT(false, "FilterNodeWebgl required as input"); return; } int32_t inputIndex = InputIndex(aIndex); if (inputIndex < 0 || !ReserveInputIndex(inputIndex)) { gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex; return; } auto* webglFilter = static_cast(aFilter); mInputFilters[inputIndex] = webglFilter; mInputSurfaces[inputIndex] = nullptr; if (mSoftwareFilter) { MOZ_ASSERT(!webglFilter || webglFilter->mSoftwareFilter); mSoftwareFilter->SetInput( aIndex, webglFilter ? webglFilter->mSoftwareFilter.get() : nullptr); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, bool aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, Float aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Size& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntSize& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntPoint& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const IntRect& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Point3D& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const DeviceColor& aValue) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValue); } } void FilterNodeWebgl::SetAttribute(uint32_t aIndex, const Float* aValues, uint32_t aSize) { if (mSoftwareFilter) { mSoftwareFilter->SetAttribute(aIndex, aValues, aSize); } } IntRect FilterNodeWebgl::MapRectToSource(const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { if (mSoftwareFilter) { if (aSourceNode && aSourceNode->GetBackendType() == FILTER_BACKEND_WEBGL) { aSourceNode = static_cast(aSourceNode)->mSoftwareFilter; } return mSoftwareFilter->MapRectToSource(aRect, aMax, aSourceNode); } return aMax; } void FilterNodeWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { ResolveAllInputs(aDT, aParent); MOZ_ASSERT(mSoftwareFilter); aDT->DrawFilterFallback(mSoftwareFilter, aSourceRect, aDestPoint, aOptions); } already_AddRefed FilterNodeWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { ResolveAllInputs(aDT, aParent); MOZ_ASSERT(mSoftwareFilter); RefPtr swDT = aDT->mSkia->CreateSimilarDrawTarget( IntSize::Ceil(aSourceRect.Size()), aDT->GetFormat()); if (!swDT) { return nullptr; } swDT->DrawFilter(mSoftwareFilter, aSourceRect, Point(0, 0), aOptions); aSurfaceOffset = aSourceRect.TopLeft(); aColor = DeviceColor(1, 1, 1, 1); return swDT->Snapshot(); } IntRect FilterNodeWebgl::MapInputRectToSource(uint32_t aInputEnumIndex, const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { int32_t inputIndex = InputIndex(aInputEnumIndex); if (inputIndex < 0) { gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs(); return aMax; } if ((uint32_t)inputIndex < NumberOfSetInputs()) { if (RefPtr filter = mInputFilters[inputIndex]) { return filter->MapRectToSource(aRect, aMax, aSourceNode); } } if (this == aSourceNode) { return aRect; } return IntRect(); } void FilterNodeWebgl::ResolveAllInputs(DrawTargetWebgl* aDT, FilterNodeWebgl* aParent) { ResolveInputs(aDT, false, aParent); for (const auto& filter : mInputFilters) { if (filter) { filter->ResolveAllInputs(aDT, this); } } } int32_t FilterNodeCropWebgl::InputIndex(uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_CROP_IN: return 0; default: return -1; } } void FilterNodeCropWebgl::SetAttribute(uint32_t aIndex, const Rect& aValue) { MOZ_ASSERT(aIndex == ATT_CROP_RECT); Rect srcRect = aValue; srcRect.Round(); if (!srcRect.ToIntRect(&mCropRect)) { mCropRect = IntRect(); } FilterNodeWebgl::SetAttribute(aIndex, aValue); } IntRect FilterNodeCropWebgl::MapRectToSource(const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { return MapInputRectToSource(IN_CROP_IN, aRect.Intersect(mCropRect), aMax, aSourceNode); } void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_CROP_IN); if (inputIdx < NumberOfSetInputs()) { Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); if (RefPtr filter = mInputFilters[inputIdx]) { filter->Draw(aDT, croppedSource, aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(), aOptions, this); } else if (RefPtr surface = mInputSurfaces[inputIdx]) { aDT->DrawSurface(surface, croppedSource - aSourceRect.TopLeft() + aDestPoint, croppedSource, DrawSurfaceOptions(), aOptions); } } } bool FilterNodeCropWebgl::DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { uint32_t inputIdx = InputIndex(IN_CROP_IN); if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); FilterNodeWebgl* filter = mInputFilters[inputIdx]; switch (filter->GetType()) { case FilterType::COLOR_MATRIX: case FilterType::LINEAR_TRANSFER: case FilterType::TABLE_TRANSFER: // Crop filters are sometimes generated before evaluating a color matrix // filter. return filter->DrawAccel( aDT, croppedSource, aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(), aOptions, this); default: break; } } return false; } already_AddRefed FilterNodeCropWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_CROP_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr filter = mInputFilters[inputIdx]) { Rect croppedSource = aSourceRect.Intersect(Rect(mCropRect)); return filter->DrawChild(this, aDT, croppedSource, aOptions, aSurfaceOffset, aColor); } return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } return nullptr; } int32_t FilterNodeTransformWebgl::InputIndex(uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_TRANSFORM_IN: return 0; default: return -1; } } void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) { MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER); mSamplingFilter = static_cast(aValue); FilterNodeWebgl::SetAttribute(aIndex, aValue); } void FilterNodeTransformWebgl::SetAttribute(uint32_t aIndex, const Matrix& aValue) { MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX); mMatrix = aValue; FilterNodeWebgl::SetAttribute(aIndex, aValue); } IntRect FilterNodeTransformWebgl::MapRectToSource(const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { if (aRect.IsEmpty()) { return IntRect(); } Matrix inv(mMatrix); if (!inv.Invert()) { return aMax; } Rect rect = inv.TransformBounds(Rect(aRect)); rect.RoundOut(); IntRect intRect; if (!rect.ToIntRect(&intRect)) { return aMax; } return MapInputRectToSource(IN_TRANSFORM_IN, intRect, aMax, aSourceNode); } void FilterNodeTransformWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { if (!mMatrix.IsTranslation()) { FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); return; } ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr filter = mInputFilters[inputIdx]) { filter->Draw(aDT, aSourceRect - mMatrix.GetTranslation(), aDestPoint, aOptions, aParent); } else if (RefPtr surface = mInputSurfaces[inputIdx]) { aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()), aSourceRect - mMatrix.GetTranslation(), DrawSurfaceOptions(mSamplingFilter), aOptions); } } } already_AddRefed FilterNodeTransformWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { if (!mMatrix.IsIntegerTranslation()) { return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr surface = mInputSurfaces[inputIdx]) { aSurfaceOffset = mMatrix.GetTranslation().Round(); aColor = DeviceColor(1, 1, 1, aOptions.mAlpha); return surface.forget(); } return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } return nullptr; } FilterNodeDeferInputWebgl::FilterNodeDeferInputWebgl( RefPtr aPath, const Pattern& aPattern, const IntRect& aSourceRect, const Matrix& aDestTransform, const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions) : mPath(std::move(aPath)), mSourceRect(aSourceRect), mDestTransform(aDestTransform), mOptions(aOptions) { mPattern.Init(aPattern); if (aStrokeOptions) { mStrokeOptions = Some(*aStrokeOptions); if (aStrokeOptions->mDashLength > 0) { mDashPatternStorage.reset(new Float[aStrokeOptions->mDashLength]); PodCopy(mDashPatternStorage.get(), aStrokeOptions->mDashPattern, aStrokeOptions->mDashLength); mStrokeOptions->mDashPattern = mDashPatternStorage.get(); } } SetAttribute(ATT_TRANSFORM_MATRIX, Matrix::Translation(mSourceRect.TopLeft())); } void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT, bool aAccel, FilterNodeWebgl* aParent) { uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); bool hasAccel = false; if (inputIdx < NumberOfSetInputs() && mInputSurfaces[inputIdx]) { if (aAccel || (mInputMask & (1 << inputIdx))) { return; } hasAccel = true; } RefPtr surface; SurfaceFormat format = SurfaceFormat::B8G8R8A8; static const ColorPattern maskPattern(DeviceColor(1, 1, 1, 1)); const Pattern* pattern = mPattern.GetPattern(); if (aAccel) { // If using acceleration on a color pattern, attempt to blur solely on the // alpha values to significantly reduce data churn, as the color will only // vary linearly with alpha over the input surface. The color will be // incorporated on the final mask draw. if (mPattern.GetPattern()->GetType() == PatternType::COLOR) { format = SurfaceFormat::A8; pattern = &maskPattern; } surface = aDT->ResolveFilterInputAccel( mPath, *pattern, mSourceRect, mDestTransform, mOptions, mStrokeOptions.ptrOr(nullptr), format); } if (!surface) { surface = aDT->mSkia->ResolveFilterInput( mPath, *pattern, mSourceRect, mDestTransform, mOptions, mStrokeOptions.ptrOr(nullptr), format); } if (hasAccel) { SetInputSoftware(inputIdx, surface); } else if (surface && surface->GetFormat() == SurfaceFormat::A8) { SetInputAccel(inputIdx, surface); } else { SetInput(inputIdx, surface); } } void FilterNodeDeferInputWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { const Pattern* pattern = mPattern.GetPattern(); AutoRestoreTransform restore(aDT); aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); aDT->ConcatTransform( Matrix(mDestTransform).PostTranslate(aDestPoint - aSourceRect.TopLeft())); DrawOptions options(aOptions.mAlpha * mOptions.mAlpha, aOptions.mCompositionOp, mOptions.mAntialiasMode); if (mStrokeOptions) { aDT->Stroke(mPath, *pattern, *mStrokeOptions, options); } else { aDT->Fill(mPath, *pattern, options); } aDT->PopClip(); } already_AddRefed FilterNodeDeferInputWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr surface = mInputSurfaces[inputIdx]) { aSurfaceOffset = mMatrix.GetTranslation().Round(); // If the output will be a mask, then supply the color that should be // rendered with it. aColor = mPattern.GetPattern()->GetType() == PatternType::COLOR ? static_cast(mPattern.GetPattern())->mColor : DeviceColor(1, 1, 1, 1); aColor.a *= aOptions.mAlpha; return surface.forget(); } return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } return nullptr; } int32_t FilterNodeGaussianBlurWebgl::InputIndex( uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_GAUSSIAN_BLUR_IN: return 0; default: return -1; } } void FilterNodeGaussianBlurWebgl::SetAttribute(uint32_t aIndex, float aValue) { MOZ_ASSERT(aIndex == ATT_GAUSSIAN_BLUR_STD_DEVIATION); // Match the FilterNodeSoftware blur limit. mStdDeviation = std::clamp(aValue, 0.0f, 100.0f); FilterNodeWebgl::SetAttribute(aIndex, aValue); } IntRect FilterNodeGaussianBlurWebgl::MapRectToSource(const IntRect& aRect, const IntRect& aMax, FilterNode* aSourceNode) { return MapInputRectToSource(IN_GAUSSIAN_BLUR_IN, aRect, aMax, aSourceNode); } void FilterNodeGaussianBlurWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_GAUSSIAN_BLUR_IN); if (inputIdx < NumberOfSetInputs()) { bool success = false; Point surfaceOffset; DeviceColor color(1, 1, 1, 1); if (RefPtr surface = mInputFilters[inputIdx] ? mInputFilters[inputIdx]->DrawChild( this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color) : mInputSurfaces[inputIdx]) { aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); IntRect surfRect = RoundedOut( Rect(surface->GetRect()).Intersect(aSourceRect - surfaceOffset)); Point destOffset = aDestPoint + Point(surfRect.TopLeft()) + surfaceOffset - aSourceRect.TopLeft(); success = surfRect.IsEmpty() || aDT->BlurSurface(mStdDeviation, surface, surfRect, destOffset, aOptions, color); aDT->PopClip(); } if (!success) { FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); } } } void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) { MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX); mMatrix = aValue; FilterNodeWebgl::SetAttribute(aIndex, aValue); } void FilterNodeColorMatrixWebgl::SetAttribute(uint32_t aIndex, uint32_t aValue) { MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE); mAlphaMode = (AlphaMode)aValue; FilterNodeWebgl::SetAttribute(aIndex, aValue); } int32_t FilterNodeColorMatrixWebgl::InputIndex(uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_COLOR_MATRIX_IN: return 0; default: return -1; } } static bool DrawColorMatrixFilter(DrawTargetWebgl* aDT, const Point& aDestPoint, const DrawOptions& aOptions, const RefPtr& aSurface, const Rect& aSourceRect, const Point& aSurfaceOffset, const Matrix5x4& aMatrix, const DeviceColor& aColor) { IntRect surfRect = RoundedOut( Rect(aSurface->GetRect()).Intersect(aSourceRect - aSurfaceOffset)); if (surfRect.IsEmpty()) { return true; } aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size())); Point destOffset = aDestPoint + Point(surfRect.TopLeft()) + aSurfaceOffset - aSourceRect.TopLeft(); bool success = true; if (aSurface->GetFormat() == SurfaceFormat::A8) { // Mask surfaces only use a solid color that is supplied outside the // surface. This color can be transformed without requiring a shader. Point4D outColor = Matrix4x4(aMatrix.components) .TransformPoint(Point4D(aColor.r, aColor.g, aColor.b, aColor.a)) + Point4D(aMatrix._51, aMatrix._52, aMatrix._53, aMatrix._54); SurfacePattern maskPattern(aSurface, ExtendMode::CLAMP, Matrix::Translation(destOffset)); if (!surfRect.IsEqualEdges(aSurface->GetRect())) { maskPattern.mSamplingRect = surfRect; } aDT->Mask(ColorPattern( DeviceColor(outColor.x, outColor.y, outColor.z, outColor.w)), maskPattern, aOptions); } else { // For normal surfaces, try to use the color matrix filter shader. success = aDT->FilterSurface(aMatrix, aSurface, surfRect, destOffset, aOptions); } aDT->PopClip(); return success; } bool FilterNodeColorMatrixWebgl::DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { if (!aParent || mAlphaMode != ALPHA_MODE_STRAIGHT) { return false; } switch (aParent->GetType()) { case FilterType::PREMULTIPLY: case FilterType::CROP: break; default: return false; } ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_COLOR_MATRIX_IN); if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { FilterNodeWebgl* filter = mInputFilters[inputIdx]; if (filter->GetType() == FilterType::UNPREMULTIPLY) { bool success = false; Point surfaceOffset; DeviceColor color; if (RefPtr surface = filter->DrawChild( this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) { success = DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface, aSourceRect, surfaceOffset, mMatrix, color); } return success; } } return false; } void FilterNodeComponentTransferWebgl::SetAttribute(uint32_t aIndex, bool aValue) { switch (aIndex) { case ATT_TRANSFER_DISABLE_R: mDisableR = aValue; break; case ATT_TRANSFER_DISABLE_G: mDisableG = aValue; break; case ATT_TRANSFER_DISABLE_B: mDisableB = aValue; break; case ATT_TRANSFER_DISABLE_A: mDisableA = aValue; break; default: gfxDevCrash(LogReason::FilterInputError) << "FilterNodeComponentTransferWebgl: Invalid attribute " << aIndex; break; } FilterNodeWebgl::SetAttribute(aIndex, aValue); } int32_t FilterNodeComponentTransferWebgl::InputIndex( uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_TRANSFER_IN: return 0; default: return -1; } } bool FilterNodeComponentTransferWebgl::DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { if (!aParent) { return false; } switch (aParent->GetType()) { case FilterType::PREMULTIPLY: case FilterType::CROP: break; default: return false; } Matrix5x4 mat5x4; if (!ToColorMatrix(mat5x4)) { return false; } ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_TRANSFER_IN); if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { FilterNodeWebgl* filter = mInputFilters[inputIdx]; if (filter->GetType() == FilterType::UNPREMULTIPLY) { bool success = false; Point surfaceOffset; DeviceColor color; if (RefPtr surface = filter->DrawChild( this, aDT, aSourceRect, DrawOptions(), surfaceOffset, color)) { success = DrawColorMatrixFilter(aDT, aDestPoint, aOptions, surface, aSourceRect, surfaceOffset, mat5x4, color); } return success; } } return false; } void FilterNodeLinearTransferWebgl::SetAttribute(uint32_t aIndex, Float aValue) { switch (aIndex) { case ATT_LINEAR_TRANSFER_SLOPE_R: mSlope.r = aValue; break; case ATT_LINEAR_TRANSFER_INTERCEPT_R: mIntercept.r = aValue; break; case ATT_LINEAR_TRANSFER_SLOPE_G: mSlope.g = aValue; break; case ATT_LINEAR_TRANSFER_INTERCEPT_G: mIntercept.g = aValue; break; case ATT_LINEAR_TRANSFER_SLOPE_B: mSlope.b = aValue; break; case ATT_LINEAR_TRANSFER_INTERCEPT_B: mIntercept.b = aValue; break; case ATT_LINEAR_TRANSFER_SLOPE_A: mSlope.a = aValue; break; case ATT_LINEAR_TRANSFER_INTERCEPT_A: mIntercept.a = aValue; break; default: MOZ_ASSERT(false); break; } FilterNodeWebgl::SetAttribute(aIndex, aValue); } bool FilterNodeLinearTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const { // Linear filters can be interpreted as a scale and translation matrix. aMatrix = Matrix5x4(); if (!mDisableR) { aMatrix._11 = mSlope.r; aMatrix._51 = mIntercept.r; } if (!mDisableG) { aMatrix._22 = mSlope.g; aMatrix._52 = mIntercept.g; } if (!mDisableB) { aMatrix._33 = mSlope.b; aMatrix._53 = mIntercept.b; } if (!mDisableA) { aMatrix._44 = mSlope.a; aMatrix._54 = mIntercept.a; } return true; } void FilterNodeTableTransferWebgl::SetAttribute(uint32_t aIndex, const Float* aValues, uint32_t aSize) { std::vector table(aValues, aValues + aSize); switch (aIndex) { case ATT_TABLE_TRANSFER_TABLE_R: mTableR = std::move(table); break; case ATT_TABLE_TRANSFER_TABLE_G: mTableG = std::move(table); break; case ATT_TABLE_TRANSFER_TABLE_B: mTableB = std::move(table); break; case ATT_TABLE_TRANSFER_TABLE_A: mTableA = std::move(table); break; default: MOZ_ASSERT(false); break; } FilterNodeWebgl::SetAttribute(aIndex, aValues, aSize); } bool FilterNodeTableTransferWebgl::ToColorMatrix(Matrix5x4& aMatrix) const { // 2 element table transfers are effectively linear transfers. These can be // interpreted as a scale and translation matrix. if ((mDisableR || mTableR.size() == 2) && (mDisableG || mTableG.size() == 2) && (mDisableB || mTableB.size() == 2) && (mDisableA || mTableA.size() == 2)) { aMatrix = Matrix5x4(); if (!mDisableR) { aMatrix._11 = mTableR[1] - mTableR[0]; aMatrix._51 = mTableR[0]; } if (!mDisableG) { aMatrix._22 = mTableG[1] - mTableG[0]; aMatrix._52 = mTableG[0]; } if (!mDisableB) { aMatrix._33 = mTableB[1] - mTableB[0]; aMatrix._53 = mTableB[0]; } if (!mDisableA) { aMatrix._44 = mTableA[1] - mTableA[0]; aMatrix._54 = mTableA[0]; } return true; } return true; } int32_t FilterNodePremultiplyWebgl::InputIndex(uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_PREMULTIPLY_IN: return 0; default: return -1; } } void FilterNodePremultiplyWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { uint32_t inputIdx = InputIndex(IN_PREMULTIPLY_IN); if (inputIdx < NumberOfSetInputs() && mInputFilters[inputIdx]) { FilterNodeWebgl* filter = mInputFilters[inputIdx]; switch (filter->GetType()) { case FilterType::CROP: case FilterType::COLOR_MATRIX: case FilterType::LINEAR_TRANSFER: case FilterType::TABLE_TRANSFER: // For color matrix filters, they will normally be preceded by a premul // filter. In certain cases, after the premul there is a crop before the // color matrix filter is actually evaluated. Here, we use DrawAccel to // only handle the filter if it can actually be accelerated, otherwise // falling back to a software color matrix filter below. if (filter->DrawAccel(aDT, aSourceRect, aDestPoint, aOptions, this)) { return; } break; default: break; } } FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions, aParent); } int32_t FilterNodeUnpremultiplyWebgl::InputIndex( uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_UNPREMULTIPLY_IN: return 0; default: return -1; } } already_AddRefed FilterNodeUnpremultiplyWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { switch (aParent->GetType()) { case FilterType::COLOR_MATRIX: case FilterType::LINEAR_TRANSFER: case FilterType::TABLE_TRANSFER: // Unpremul should always be the child of a color matrix filter. break; default: return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_UNPREMULTIPLY_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr filter = mInputFilters[inputIdx]) { if (RefPtr surface = filter->DrawChild( this, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor)) { return surface.forget(); } } else if (RefPtr surface = mInputSurfaces[inputIdx]) { aColor = DeviceColor(1, 1, 1, aOptions.mAlpha); return surface.forget(); } return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } return nullptr; } int32_t FilterNodeOpacityWebgl::InputIndex(uint32_t aInputEnumIndex) const { switch (aInputEnumIndex) { case IN_OPACITY_IN: return 0; default: return -1; } } void FilterNodeOpacityWebgl::SetAttribute(uint32_t aIndex, Float aValue) { MOZ_ASSERT(aIndex == ATT_OPACITY_VALUE); mValue = aValue; FilterNodeWebgl::SetAttribute(aIndex, aValue); } void FilterNodeOpacityWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions, FilterNodeWebgl* aParent) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_OPACITY_IN); if (inputIdx < NumberOfSetInputs()) { // Opacity filters need only modify the DrawOptions alpha value. DrawOptions options(aOptions); options.mAlpha *= mValue; if (RefPtr filter = mInputFilters[inputIdx]) { filter->Draw(aDT, aSourceRect, aDestPoint, options, this); } else if (RefPtr surface = mInputSurfaces[inputIdx]) { aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()), aSourceRect, DrawSurfaceOptions(), options); } } } already_AddRefed FilterNodeOpacityWebgl::DrawChild( FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect, const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) { ResolveInputs(aDT, true, aParent); uint32_t inputIdx = InputIndex(IN_OPACITY_IN); if (inputIdx < NumberOfSetInputs()) { if (RefPtr filter = mInputFilters[inputIdx]) { // Opacity filters need only modify he DrawOptions alpha value. DrawOptions options(aOptions); options.mAlpha *= mValue; return filter->DrawChild(this, aDT, aSourceRect, options, aSurfaceOffset, aColor); } return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor); } return nullptr; } } // namespace mozilla::gfx