replaced by .editorconfig # ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D287843
1052 lines
35 KiB
C++
1052 lines
35 KiB
C++
/* 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 <limits>
|
|
|
|
#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<FilterNodeSoftware>()) {}
|
|
|
|
FilterNodeWebgl::~FilterNodeWebgl() = default;
|
|
|
|
already_AddRefed<FilterNodeWebgl> FilterNodeWebgl::Create(FilterType aType) {
|
|
RefPtr<FilterNodeWebgl> 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<size_t>::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<FilterNodeWebgl*>(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<FilterNodeWebgl*>(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<SourceSurface> FilterNodeWebgl::DrawChild(
|
|
FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
|
|
const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor) {
|
|
ResolveAllInputs(aDT, aParent);
|
|
|
|
MOZ_ASSERT(mSoftwareFilter);
|
|
RefPtr<DrawTarget> 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<FilterNodeWebgl> 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<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
|
|
filter->Draw(aDT, croppedSource,
|
|
aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(),
|
|
aOptions, this);
|
|
} else if (RefPtr<SourceSurface> 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<SourceSurface> 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<FilterNodeWebgl> 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<SamplingFilter>(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<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
|
|
filter->Draw(aDT, aSourceRect - mMatrix.GetTranslation(), aDestPoint,
|
|
aOptions, aParent);
|
|
} else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
|
|
aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()),
|
|
aSourceRect - mMatrix.GetTranslation(),
|
|
DrawSurfaceOptions(mSamplingFilter), aOptions);
|
|
}
|
|
}
|
|
}
|
|
|
|
already_AddRefed<SourceSurface> 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<SourceSurface> 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<Path> 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<SourceSurface> 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<SourceSurface> 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<SourceSurface> 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<const ColorPattern*>(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<SourceSurface> 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<SourceSurface>& 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<SourceSurface> 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<SourceSurface> 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<Float> 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<SourceSurface> 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<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
|
|
if (RefPtr<SourceSurface> surface = filter->DrawChild(
|
|
this, aDT, aSourceRect, aOptions, aSurfaceOffset, aColor)) {
|
|
return surface.forget();
|
|
}
|
|
} else if (RefPtr<SourceSurface> 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<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
|
|
filter->Draw(aDT, aSourceRect, aDestPoint, options, this);
|
|
} else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
|
|
aDT->DrawSurface(surface, Rect(aDestPoint, aSourceRect.Size()),
|
|
aSourceRect, DrawSurfaceOptions(), options);
|
|
}
|
|
}
|
|
}
|
|
|
|
already_AddRefed<SourceSurface> 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<FilterNodeWebgl> 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
|