Source code

Revision control

Copy as Markdown

Other Tools

/* 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