commit 113c00a6ef404c2e36ee4b159fe8cd59108046ec
parent 30ff531034cbeb7dfc3ebc4ec0a84a3a97fda162
Author: Lee Salzman <lsalzman@mozilla.com>
Date: Mon, 6 Oct 2025 18:16:45 +0000
Bug 1947364 - Support color matrix filters in Accelerated Canvas2D. r=aosmond
This implements support for accelerated color matrix filters in AC2D. First,
it provides a new shader for handling color matrices. Then, it extends
FilterNodeWebgl to route filter rendering requests to this new shader as
appropriate.
This, with the previously implemented guassian blur filter support, should
handle the majority of canvas filter requests that don't use external SVG
filters.
Differential Revision: https://phabricator.services.mozilla.com/D267531
Diffstat:
5 files changed, 1160 insertions(+), 109 deletions(-)
diff --git a/dom/canvas/DrawTargetWebgl.cpp b/dom/canvas/DrawTargetWebgl.cpp
@@ -1767,6 +1767,92 @@ bool SharedContextWebgl::CreateShaders() {
UniformData(LOCAL_GL_INT, mBlurProgramSampler, Array<int32_t, 1>{0});
UniformData(LOCAL_GL_INT, mBlurProgramClipMask, Array<int32_t, 1>{1});
}
+ if (!mFilterProgram) {
+ auto vsSource =
+ "uniform vec2 u_viewport;\n"
+ "uniform vec4 u_clipbounds;\n"
+ "uniform vec4 u_transform;\n"
+ "uniform vec4 u_texmatrix;\n"
+ "attribute vec3 a_vertex;\n"
+ "varying vec2 v_cliptc;\n"
+ "varying vec2 v_texcoord;\n"
+ "varying vec4 v_clipdist;\n"
+ "void main() {\n"
+ " vec2 vertex = u_transform.xy * a_vertex.xy + u_transform.zw;\n"
+ " gl_Position = vec4(vertex * 2.0 / u_viewport - 1.0, 0.0, 1.0);\n"
+ " v_cliptc = vertex / u_viewport;\n"
+ " v_clipdist = vec4(vertex - u_clipbounds.xy,\n"
+ " u_clipbounds.zw - vertex);\n"
+ " v_texcoord = u_texmatrix.xy * a_vertex.xy + u_texmatrix.zw;\n"
+ "}\n";
+ auto fsSource =
+ "precision mediump float;\n"
+ "uniform vec4 u_texbounds;\n"
+ "uniform mat4 u_colormatrix;\n"
+ "uniform vec4 u_coloroffset;\n"
+ "uniform sampler2D u_sampler;\n"
+ "uniform sampler2D u_clipmask;\n"
+ "varying highp vec2 v_cliptc;\n"
+ "varying highp vec2 v_texcoord;\n"
+ "varying vec4 v_clipdist;\n"
+ "bool check_bounds(vec2 tc) {\n"
+ " return all(greaterThanEqual(\n"
+ " vec4(tc, u_texbounds.zw), vec4(u_texbounds.xy, tc)));\n"
+ "}\n"
+ "void main() {\n"
+ " vec4 color = check_bounds(v_texcoord) ?\n"
+ " texture2D(u_sampler, v_texcoord) : vec4(0.0);\n"
+ " if (color.a != 0.0) color.rgb /= color.a;\n"
+ " color = clamp(u_colormatrix * color + u_coloroffset, 0.0, 1.0);\n"
+ " color.rgb *= color.a;\n"
+ " float clip = texture2D(u_clipmask, v_cliptc).r;\n"
+ " vec2 dist = min(v_clipdist.xy, v_clipdist.zw);\n"
+ " float aa = clamp(min(dist.x, dist.y), 0.0, 1.0);\n"
+ " gl_FragColor = clip * aa * color;\n"
+ "}\n";
+ RefPtr<WebGLShader> vsId = mWebgl->CreateShader(LOCAL_GL_VERTEX_SHADER);
+ mWebgl->ShaderSource(*vsId, vsSource);
+ mWebgl->CompileShader(*vsId);
+ if (!mWebgl->GetCompileResult(*vsId).success) {
+ return false;
+ }
+ RefPtr<WebGLShader> fsId = mWebgl->CreateShader(LOCAL_GL_FRAGMENT_SHADER);
+ mWebgl->ShaderSource(*fsId, fsSource);
+ mWebgl->CompileShader(*fsId);
+ if (!mWebgl->GetCompileResult(*fsId).success) {
+ return false;
+ }
+ mFilterProgram = mWebgl->CreateProgram();
+ mWebgl->AttachShader(*mFilterProgram, *vsId);
+ mWebgl->AttachShader(*mFilterProgram, *fsId);
+ mWebgl->BindAttribLocation(*mFilterProgram, 0, "a_vertex");
+ mWebgl->LinkProgram(*mFilterProgram);
+ if (!mWebgl->GetLinkResult(*mFilterProgram).success) {
+ return false;
+ }
+ mFilterProgramViewport = GetUniformLocation(mFilterProgram, "u_viewport");
+ mFilterProgramTransform = GetUniformLocation(mFilterProgram, "u_transform");
+ mFilterProgramTexMatrix = GetUniformLocation(mFilterProgram, "u_texmatrix");
+ mFilterProgramTexBounds = GetUniformLocation(mFilterProgram, "u_texbounds");
+ mFilterProgramColorMatrix =
+ GetUniformLocation(mFilterProgram, "u_colormatrix");
+ mFilterProgramColorOffset =
+ GetUniformLocation(mFilterProgram, "u_coloroffset");
+ mFilterProgramSampler = GetUniformLocation(mFilterProgram, "u_sampler");
+ mFilterProgramClipMask = GetUniformLocation(mFilterProgram, "u_clipmask");
+ mFilterProgramClipBounds =
+ GetUniformLocation(mFilterProgram, "u_clipbounds");
+ if (!mFilterProgramViewport || !mFilterProgramTransform ||
+ !mFilterProgramTexMatrix || !mFilterProgramTexBounds ||
+ !mFilterProgramColorMatrix || !mFilterProgramColorOffset ||
+ !mFilterProgramSampler || !mFilterProgramClipMask ||
+ !mFilterProgramClipBounds) {
+ return false;
+ }
+ mWebgl->UseProgram(mFilterProgram);
+ UniformData(LOCAL_GL_INT, mFilterProgramSampler, Array<int32_t, 1>{0});
+ UniformData(LOCAL_GL_INT, mFilterProgramClipMask, Array<int32_t, 1>{1});
+ }
return true;
}
@@ -2446,7 +2532,7 @@ void SharedContextWebgl::BindScratchFramebuffer(TextureHandle* aHandle,
// sub-texture of a larger shared texture.
already_AddRefed<TextureHandle> SharedContextWebgl::AllocateTextureHandle(
SurfaceFormat aFormat, const IntSize& aSize, bool aAllowShared,
- bool aRenderable, BackingTexture* aAvoid) {
+ bool aRenderable, const WebGLTexture* aAvoid) {
RefPtr<TextureHandle> handle;
// Calculate the bytes that would be used by this texture handle, and prune
// enough other textures to ensure we have that much usable texture space
@@ -2463,7 +2549,8 @@ already_AddRefed<TextureHandle> SharedContextWebgl::AllocateTextureHandle(
// from that if possible.
for (auto& shared : mSharedTextures) {
if (shared->GetFormat() == aFormat &&
- shared->IsRenderable() == aRenderable && shared != aAvoid) {
+ shared->IsRenderable() == aRenderable &&
+ shared->GetWebGLTexture() != aAvoid) {
bool wasEmpty = !shared->HasAllocatedHandles();
handle = shared->Allocate(aSize);
if (handle) {
@@ -2946,6 +3033,16 @@ bool SharedContextWebgl::DrawRectAccel(
nullptr);
}
}
+ if (handle) {
+ BackingTexture* backing = handle->GetBackingTexture();
+ if (!tex) {
+ tex = backing->GetWebGLTexture();
+ }
+ bounds = bounds.IsEmpty() ? handle->GetBounds()
+ : handle->GetBounds().SafeIntersect(
+ bounds + handle->GetBounds().TopLeft());
+ backingSize = backing->GetSize();
+ }
// Map the composition op to a WebGL blend mode, if possible. If there is
// a mask color and a texture with multiple channels, assume subpixel
@@ -3008,16 +3105,6 @@ bool SharedContextWebgl::DrawRectAccel(
mImageProgramUniformState.mSwizzle);
// Start binding the WebGL state for the texture.
- if (handle) {
- BackingTexture* backing = handle->GetBackingTexture();
- if (!tex) {
- tex = backing->GetWebGLTexture();
- }
- bounds = bounds.IsEmpty() ? handle->GetBounds()
- : handle->GetBounds().SafeIntersect(
- bounds + handle->GetBounds().TopLeft());
- backingSize = backing->GetSize();
- }
if (mLastTexture != tex) {
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
mLastTexture = tex;
@@ -3097,14 +3184,11 @@ bool SharedContextWebgl::DrawRectAccel(
return success;
}
-// Provides a single pass of a separable blur.
-bool SharedContextWebgl::BlurRectPass(
- const Rect& aDestRect, const Point& aSigma, bool aHorizontal,
+// Get an appropriate input texture for a given surface within the source rect.
+already_AddRefed<WebGLTexture> SharedContextWebgl::GetFilterInputTexture(
const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
- const DrawOptions& aOptions, Maybe<DeviceColor> aMaskColor,
- RefPtr<TextureHandle>* aHandle, RefPtr<TextureHandle>* aTargetHandle,
- bool aFilter) {
- // Now try to actually draw the pattern...
+ RefPtr<TextureHandle>* aHandle, IntPoint& aOffset, SurfaceFormat& aFormat,
+ IntRect& aBounds, IntSize& aBackingSize) {
// If a texture handle was supplied, or if the surface already has an
// assigned texture handle stashed in its used data, try to use it.
RefPtr<SourceSurface> underlyingSurface =
@@ -3132,7 +3216,7 @@ bool SharedContextWebgl::BlurRectPass(
if (!underlyingSurface) {
// If there was no actual surface supplied, then we tried to draw
// using a texture handle, but the texture handle wasn't valid.
- return false;
+ return nullptr;
}
texSize = underlyingSurface->GetSize();
format = underlyingSurface->GetFormat();
@@ -3160,7 +3244,7 @@ bool SharedContextWebgl::BlurRectPass(
// If we get here, we need a data surface for a texture upload.
RefPtr<DataSourceSurface> data = underlyingSurface->GetDataSurface();
if (!data) {
- return false;
+ return nullptr;
}
// There is no existing handle. Try to allocate a new one. If the
// surface size may change via a forced update, then don't allocate
@@ -3168,7 +3252,7 @@ bool SharedContextWebgl::BlurRectPass(
handle = AllocateTextureHandle(format, texSize);
if (!handle) {
MOZ_ASSERT(false);
- return false;
+ return nullptr;
}
UploadSurfaceToHandle(data, offset, handle);
// Link the handle to the surface's user data.
@@ -3181,6 +3265,229 @@ bool SharedContextWebgl::BlurRectPass(
}
}
+ if (handle) {
+ BackingTexture* backing = handle->GetBackingTexture();
+ if (!tex) {
+ tex = backing->GetWebGLTexture();
+ }
+ bounds = bounds.IsEmpty() ? handle->GetBounds()
+ : handle->GetBounds().SafeIntersect(
+ bounds + handle->GetBounds().TopLeft());
+ backingSize = backing->GetSize();
+ }
+
+ aOffset = offset;
+ aFormat = format;
+ aBounds = bounds;
+ aBackingSize = backingSize;
+ return tex.forget();
+}
+
+// Implements any filter that can be computed with a 5x4 color matrix.
+bool SharedContextWebgl::FilterRect(const Rect& aDestRect,
+ const Matrix5x4& aColorMatrix,
+ const RefPtr<SourceSurface>& aSurface,
+ const IntRect& aSourceRect,
+ const DrawOptions& aOptions,
+ RefPtr<TextureHandle>* aHandle,
+ RefPtr<TextureHandle>* aTargetHandle) {
+ if (!aTargetHandle && !mCurrentTarget->MarkChanged()) {
+ return false;
+ }
+
+ IntPoint offset;
+ SurfaceFormat format;
+ IntRect bounds;
+ IntSize backingSize;
+ RefPtr<WebGLTexture> tex = GetFilterInputTexture(
+ aSurface, aSourceRect, aHandle, offset, format, bounds, backingSize);
+ if (!tex) {
+ return false;
+ }
+
+ IntSize viewportSize = mViewportSize;
+ bool needTarget = !!aTargetHandle;
+ if (needTarget) {
+ IntSize targetSize = IntSize::Ceil(aDestRect.Size());
+ viewportSize = targetSize;
+ // Blur filters need to render to a color target, whereas shadows will only
+ // sample alpha.
+ // If sourcing from a texture handle as input, be careful not to render to
+ // a handle with the same exact backing texture, which is not allowed in
+ // WebGL.
+ RefPtr<TextureHandle> targetHandle =
+ AllocateTextureHandle(format, targetSize, true, true, tex);
+ if (!targetHandle) {
+ MOZ_ASSERT(false);
+ return false;
+ }
+
+ *aTargetHandle = targetHandle;
+
+ BindScratchFramebuffer(targetHandle, true, targetSize);
+
+ SetBlendState(CompositionOp::OP_OVER);
+ } else {
+ // Set up the scissor test to reflect the clipping rectangle, if supplied.
+ if (!mClipRect.Contains(IntRect(IntPoint(), mViewportSize))) {
+ EnableScissor(mClipRect);
+ } else {
+ DisableScissor();
+ }
+
+ // Map the composition op to a WebGL blend mode, if possible.
+ SetBlendState(aOptions.mCompositionOp);
+ }
+
+ // Switch to the filter shader and set up relevant transforms.
+ if (mLastProgram != mFilterProgram) {
+ mWebgl->UseProgram(mFilterProgram);
+ mLastProgram = mFilterProgram;
+ }
+
+ Array<float, 2> viewportData = {float(viewportSize.width),
+ float(viewportSize.height)};
+ MaybeUniformData(LOCAL_GL_FLOAT_VEC2, mFilterProgramViewport, viewportData,
+ mFilterProgramUniformState.mViewport);
+
+ Rect xformRect;
+ if (needTarget) {
+ // If rendering to a temporary target for an intermediate pass, then fill
+ // the entire framebuffer.
+ xformRect = Rect(IntRect(IntPoint(), viewportSize));
+ } else {
+ // If doing a final composite, then render to the requested rectangle.
+ xformRect = aDestRect;
+ }
+ Array<float, 4> xformData = {xformRect.width, xformRect.height, xformRect.x,
+ xformRect.y};
+ MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTransform, xformData,
+ mFilterProgramUniformState.mTransform);
+
+ Rect clipRect;
+ if (needTarget) {
+ // Disable any AA clipping.
+ clipRect = xformRect;
+ } else {
+ clipRect = mClipAARect;
+ }
+ // Offset the clip AA bounds by 0.5 to ensure AA falls to 0 at pixel
+ // boundary.
+ clipRect.Inflate(0.5f);
+ Array<float, 4> clipData = {clipRect.x, clipRect.y, clipRect.XMost(),
+ clipRect.YMost()};
+ MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramClipBounds, clipData,
+ mFilterProgramUniformState.mClipBounds);
+
+ Array<float, 16> colorMatData = {
+ aColorMatrix._11, aColorMatrix._12, aColorMatrix._13, aColorMatrix._14,
+ aColorMatrix._21, aColorMatrix._22, aColorMatrix._23, aColorMatrix._24,
+ aColorMatrix._31, aColorMatrix._32, aColorMatrix._33, aColorMatrix._34,
+ aColorMatrix._41, aColorMatrix._42, aColorMatrix._43, aColorMatrix._44};
+ MaybeUniformData(LOCAL_GL_FLOAT_MAT4, mFilterProgramColorMatrix, colorMatData,
+ mFilterProgramUniformState.mColorMatrix);
+ Array<float, 4> colorOffData = {aColorMatrix._51, aColorMatrix._52,
+ aColorMatrix._53, aColorMatrix._54};
+ MaybeUniformData(LOCAL_GL_FLOAT_MAT4, mFilterProgramColorOffset, colorOffData,
+ mFilterProgramUniformState.mColorOffset);
+
+ // Start binding the WebGL state for the texture.
+ if (mLastTexture != tex) {
+ mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
+ mLastTexture = tex;
+ }
+
+ // Set up the texture coordinate matrix to map from the input rectangle to
+ // the backing texture subrect.
+ Size backingSizeF(backingSize);
+ Rect uvXform((bounds.x - offset.x) / backingSizeF.width,
+ (bounds.y - offset.y) / backingSizeF.height,
+ xformRect.width / backingSizeF.width,
+ xformRect.height / backingSizeF.height);
+ Array<float, 4> uvData = {uvXform.width, uvXform.height, uvXform.x,
+ uvXform.y};
+ MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTexMatrix, uvData,
+ mFilterProgramUniformState.mTexMatrix);
+
+ // Bounds for inclusion testing. These are not offset by half a pixel because
+ // they are not used for clamping, but rather denote pixel thresholds.
+ Array<float, 4> texBounds = {
+ bounds.x / backingSizeF.width,
+ bounds.y / backingSizeF.height,
+ bounds.XMost() / backingSizeF.width,
+ bounds.YMost() / backingSizeF.height,
+ };
+ MaybeUniformData(LOCAL_GL_FLOAT_VEC4, mFilterProgramTexBounds, texBounds,
+ mFilterProgramUniformState.mTexBounds);
+
+ RefPtr<WebGLTexture> prevClipMask;
+ if (needTarget) {
+ // Ensure the current clip mask is ignored.
+ prevClipMask = mLastClipMask;
+ SetNoClipMask();
+ }
+
+ DrawQuad();
+
+ if (needTarget) {
+ // Restore the previous framebuffer state.
+ RestoreCurrentTarget(prevClipMask);
+ }
+
+ return true;
+}
+
+// Filters a surface and draws the result at the specified offset.
+bool DrawTargetWebgl::FilterSurface(const Matrix5x4& aColorMatrix,
+ SourceSurface* aSurface,
+ const IntRect& aSourceRect,
+ const Point& aDest,
+ const DrawOptions& aOptions) {
+ if (ShouldAccelPath(aOptions, nullptr)) {
+ IntRect sourceRect =
+ aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
+ if (mTransform.IsTranslation() &&
+ !mSharedContext->RequiresMultiStageBlend(aOptions, this)) {
+ // If the transform doesn't require resampling and the blend op is simple,
+ // then draw the filter directly to the canvas.
+ return mSharedContext->FilterRect(
+ Rect(aDest + mTransform.GetTranslation(), Size(sourceRect.Size())),
+ aColorMatrix, aSurface, sourceRect, aOptions, nullptr, nullptr);
+ }
+ // There is a complex transform or blend op, so the filter must be drawn to
+ // an intermediate texture first before resampling.
+ RefPtr<TextureHandle> resultHandle;
+ if (mSharedContext->FilterRect(Rect(Point(0, 0), Size(sourceRect.Size())),
+ aColorMatrix, aSurface, sourceRect,
+ DrawOptions(), nullptr, &resultHandle) &&
+ resultHandle) {
+ SurfacePattern filterPattern(nullptr, ExtendMode::CLAMP,
+ Matrix::Translation(aDest));
+ return mSharedContext->DrawRectAccel(
+ Rect(aDest, Size(resultHandle->GetSize())), filterPattern, aOptions,
+ Nothing(), &resultHandle, true, true, true);
+ }
+ }
+ return false;
+}
+
+// Provides a single pass of a separable blur.
+bool SharedContextWebgl::BlurRectPass(
+ const Rect& aDestRect, const Point& aSigma, bool aHorizontal,
+ const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
+ const DrawOptions& aOptions, Maybe<DeviceColor> aMaskColor,
+ RefPtr<TextureHandle>* aHandle, RefPtr<TextureHandle>* aTargetHandle,
+ bool aFilter) {
+ IntPoint offset;
+ SurfaceFormat format;
+ IntRect bounds;
+ IntSize backingSize;
+ RefPtr<WebGLTexture> tex = GetFilterInputTexture(
+ aSurface, aSourceRect, aHandle, offset, format, bounds, backingSize);
+ if (!tex) {
+ return false;
+ }
+
IntSize viewportSize = mViewportSize;
IntSize blurRadius(BLUR_ACCEL_RADIUS(aSigma.x), BLUR_ACCEL_RADIUS(aSigma.y));
bool needTarget = !!aTargetHandle;
@@ -3190,21 +3497,16 @@ bool SharedContextWebgl::BlurRectPass(
// accommodate blurred pixels in the margins.
IntSize targetSize(
int(ceil(aDestRect.width)) + blurRadius.width * 2,
- aHorizontal ? texSize.height
+ aHorizontal ? bounds.height
: int(ceil(aDestRect.height)) + blurRadius.height * 2);
viewportSize = targetSize;
+ // Blur filters need to render to a color target, whereas shadows will only
+ // sample alpha.
// If sourcing from a texture handle as input, be careful not to render to
// a handle with the same exact backing texture, which is not allowed in
// WebGL.
- BackingTexture* avoid =
- aHandle && aHandle->get()
- ? aHandle->get()->GetBackingTexture()
- : (handle ? handle->GetBackingTexture() : nullptr);
- // Blur filters need to render to a color target, whereas shadows will only
- // sample alpha.
- RefPtr<TextureHandle> targetHandle =
- AllocateTextureHandle(aFilter ? handle->GetFormat() : SurfaceFormat::A8,
- targetSize, true, true, avoid);
+ RefPtr<TextureHandle> targetHandle = AllocateTextureHandle(
+ aFilter ? format : SurfaceFormat::A8, targetSize, true, true, tex);
if (!targetHandle) {
MOZ_ASSERT(false);
return false;
@@ -3227,7 +3529,7 @@ bool SharedContextWebgl::BlurRectPass(
SetBlendState(aOptions.mCompositionOp);
}
- // Switch to the image shader and set up relevant transforms.
+ // Switch to the blur shader and set up relevant transforms.
if (mLastProgram != mBlurProgram) {
mWebgl->UseProgram(mBlurProgram);
mLastProgram = mBlurProgram;
@@ -3281,16 +3583,6 @@ bool SharedContextWebgl::BlurRectPass(
mBlurProgramUniformState.mSwizzle);
// Start binding the WebGL state for the texture.
- if (handle) {
- BackingTexture* backing = handle->GetBackingTexture();
- if (!tex) {
- tex = backing->GetWebGLTexture();
- }
- bounds = bounds.IsEmpty() ? handle->GetBounds()
- : handle->GetBounds().SafeIntersect(
- bounds + handle->GetBounds().TopLeft());
- backingSize = backing->GetSize();
- }
if (mLastTexture != tex) {
mWebgl->BindTexture(LOCAL_GL_TEXTURE_2D, tex);
mLastTexture = tex;
@@ -3399,8 +3691,7 @@ already_AddRefed<SourceSurface> SharedContextWebgl::DownscaleBlurInput(
IntSize halfSize = (sourceRect.Size() + IntSize(1, 1)) / 2;
// Allocate a half-size texture for the downscale target.
RefPtr<TextureHandle> halfHandle = AllocateTextureHandle(
- aSurface->GetFormat(), halfSize, true, true,
- fullHandle ? fullHandle->GetBackingTexture() : nullptr);
+ aSurface->GetFormat(), halfSize, true, true, fullTex);
if (!halfHandle) {
break;
}
@@ -4969,8 +5260,11 @@ void DrawTargetWebgl::Mask(const Pattern& aSource, const Pattern& aMask,
return;
}
- DrawRect(Rect(IntRect(IntPoint(), maskPattern.mSurface->GetSize())),
- maskPattern, aOptions, Some(sourceColor));
+ IntRect samplingRect = !maskPattern.mSamplingRect.IsEmpty()
+ ? maskPattern.mSamplingRect
+ : maskPattern.mSurface->GetRect();
+ DrawRect(maskPattern.mMatrix.TransformBounds(Rect(samplingRect)), maskPattern,
+ aOptions, Some(sourceColor));
}
void DrawTargetWebgl::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
diff --git a/dom/canvas/DrawTargetWebgl.h b/dom/canvas/DrawTargetWebgl.h
@@ -173,6 +173,16 @@ class SharedContextWebgl : public mozilla::RefCounted<SharedContextWebgl>,
Maybe<uint32_t> mBlurProgramSampler;
Maybe<uint32_t> mBlurProgramClipMask;
Maybe<uint32_t> mBlurProgramClipBounds;
+ RefPtr<WebGLProgram> mFilterProgram;
+ Maybe<uint32_t> mFilterProgramViewport;
+ Maybe<uint32_t> mFilterProgramTransform;
+ Maybe<uint32_t> mFilterProgramTexMatrix;
+ Maybe<uint32_t> mFilterProgramTexBounds;
+ Maybe<uint32_t> mFilterProgramColorMatrix;
+ Maybe<uint32_t> mFilterProgramColorOffset;
+ Maybe<uint32_t> mFilterProgramSampler;
+ Maybe<uint32_t> mFilterProgramClipMask;
+ Maybe<uint32_t> mFilterProgramClipBounds;
struct SolidProgramUniformState {
Maybe<Array<float, 2>> mViewport;
@@ -205,6 +215,16 @@ class SharedContextWebgl : public mozilla::RefCounted<SharedContextWebgl>,
Maybe<Array<float, 4>> mClipBounds;
} mBlurProgramUniformState;
+ struct FilterProgramUniformState {
+ Maybe<Array<float, 2>> mViewport;
+ Maybe<Array<float, 4>> mTransform;
+ Maybe<Array<float, 4>> mTexMatrix;
+ Maybe<Array<float, 4>> mTexBounds;
+ Maybe<Array<float, 16>> mColorMatrix;
+ Maybe<Array<float, 4>> mColorOffset;
+ Maybe<Array<float, 4>> mClipBounds;
+ } mFilterProgramUniformState;
+
// Scratch framebuffer used to wrap textures for miscellaneous utility ops.
RefPtr<WebGLFramebuffer> mScratchFramebuffer;
// Scratch framebuffer used to wrap textures for sub-targets.
@@ -348,7 +368,7 @@ class SharedContextWebgl : public mozilla::RefCounted<SharedContextWebgl>,
const IntSize& aViewportSize = IntSize());
already_AddRefed<TextureHandle> AllocateTextureHandle(
SurfaceFormat aFormat, const IntSize& aSize, bool aAllowShared = true,
- bool aRenderable = false, BackingTexture* aAvoid = nullptr);
+ bool aRenderable = false, const WebGLTexture* aAvoid = nullptr);
void DrawQuad();
void DrawTriangles(const PathVertexRange& aRange);
bool DrawRectAccel(const Rect& aRect, const Pattern& aPattern,
@@ -361,6 +381,16 @@ class SharedContextWebgl : public mozilla::RefCounted<SharedContextWebgl>,
const PathVertexRange* aVertexRange = nullptr,
const Matrix* aRectXform = nullptr,
uint8_t aBlendStage = 0);
+
+ already_AddRefed<WebGLTexture> GetFilterInputTexture(
+ const RefPtr<SourceSurface>& aSurface, const IntRect& aSourceRect,
+ RefPtr<TextureHandle>* aHandle, IntPoint& aOffset, SurfaceFormat& aFormat,
+ IntRect& aBounds, IntSize& aBackingSize);
+ bool FilterRect(const Rect& aDestRect, const Matrix5x4& aColorMatrix,
+ const RefPtr<SourceSurface>& aSurface,
+ const IntRect& aSourceRect, const DrawOptions& aOptions,
+ RefPtr<TextureHandle>* aHandle,
+ RefPtr<TextureHandle>* aTargetHandle = nullptr);
bool BlurRectPass(const Rect& aDestRect, const Point& aSigma,
bool aHorizontal, const RefPtr<SourceSurface>& aSurface,
const IntRect& aSourceRect,
@@ -450,7 +480,6 @@ class SharedContextWebgl : public mozilla::RefCounted<SharedContextWebgl>,
class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
friend class FilterNodeWebgl;
friend class FilterNodeDeferInputWebgl;
- friend class FilterNodeGaussianBlurWebgl;
friend class SourceSurfaceWebgl;
friend class SharedContextWebgl;
@@ -690,6 +719,14 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
const StrokeOptions* aStrokeOptions = nullptr,
SurfaceFormat aFormat = SurfaceFormat::B8G8R8A8);
+ bool FilterSurface(const Matrix5x4& aColorMatrix, SourceSurface* aSurface,
+ const IntRect& aSourceRect, const Point& aDest,
+ const DrawOptions& aOptions = DrawOptions());
+ bool BlurSurface(float aSigma, SourceSurface* aSurface,
+ const IntRect& aSourceRect, const Point& aDest,
+ const DrawOptions& aOptions = DrawOptions(),
+ const DeviceColor& aColor = DeviceColor(1, 1, 1, 1));
+
void SetTransform(const Matrix& aTransform) override;
void* GetNativeSurface(NativeSurfaceType aType) override;
@@ -757,11 +794,6 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
const DrawOptions& aOptions,
const StrokeOptions* aStrokeOptions = nullptr);
- bool BlurSurface(float aSigma, SourceSurface* aSurface,
- const IntRect& aSourceRect, const Point& aDest,
- const DrawOptions& aOptions = DrawOptions(),
- const DeviceColor& aColor = DeviceColor(1, 1, 1, 1));
-
bool MarkChanged();
bool ReadIntoSkia();
diff --git a/dom/canvas/FilterNodeWebgl.cpp b/dom/canvas/FilterNodeWebgl.cpp
@@ -37,6 +37,24 @@ already_AddRefed<FilterNodeWebgl> FilterNodeWebgl::Create(FilterType aType) {
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;
@@ -212,17 +230,18 @@ IntRect FilterNodeWebgl::MapRectToSource(const IntRect& aRect,
}
void FilterNodeWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
- const Point& aDestPoint,
- const DrawOptions& aOptions) {
- ResolveAllInputs(aDT);
+ 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(
- DrawTargetWebgl* aDT, const Rect& aSourceRect, Point& aSurfaceOffset) {
- ResolveAllInputs(aDT);
+ 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(
@@ -230,8 +249,9 @@ already_AddRefed<SourceSurface> FilterNodeWebgl::DrawChild(
if (!swDT) {
return nullptr;
}
- swDT->DrawFilter(mSoftwareFilter, aSourceRect, Point(0, 0));
+ swDT->DrawFilter(mSoftwareFilter, aSourceRect, Point(0, 0), aOptions);
aSurfaceOffset = aSourceRect.TopLeft();
+ aColor = DeviceColor(1, 1, 1, 1);
return swDT->Snapshot();
}
@@ -256,11 +276,12 @@ IntRect FilterNodeWebgl::MapInputRectToSource(uint32_t aInputEnumIndex,
return IntRect();
}
-void FilterNodeWebgl::ResolveAllInputs(DrawTargetWebgl* aDT) {
+void FilterNodeWebgl::ResolveAllInputs(DrawTargetWebgl* aDT,
+ FilterNodeWebgl* aParent) {
+ ResolveInputs(aDT, false, aParent);
for (const auto& filter : mInputFilters) {
if (filter) {
- filter->ResolveInputs(aDT, false);
- filter->ResolveAllInputs(aDT);
+ filter->ResolveAllInputs(aDT, this);
}
}
}
@@ -293,8 +314,9 @@ IntRect FilterNodeCropWebgl::MapRectToSource(const IntRect& aRect,
void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
const Point& aDestPoint,
- const DrawOptions& aOptions) {
- ResolveInputs(aDT, true);
+ const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) {
+ ResolveInputs(aDT, true, aParent);
uint32_t inputIdx = InputIndex(IN_CROP_IN);
if (inputIdx < NumberOfSetInputs()) {
@@ -302,7 +324,7 @@ void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
if (RefPtr<FilterNodeWebgl> filter = mInputFilters[inputIdx]) {
filter->Draw(aDT, croppedSource,
aDestPoint + croppedSource.TopLeft() - aSourceRect.TopLeft(),
- aOptions);
+ aOptions, this);
} else if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
aDT->DrawSurface(surface,
croppedSource - aSourceRect.TopLeft() + aDestPoint,
@@ -311,6 +333,50 @@ void FilterNodeCropWebgl::Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
}
}
+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:
@@ -352,19 +418,50 @@ IntRect FilterNodeTransformWebgl::MapRectToSource(const IntRect& aRect,
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(
- DrawTargetWebgl* aDT, const Rect& aSourceRect, Point& aSurfaceOffset) {
- ResolveInputs(aDT, true);
+ 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 (mMatrix.IsIntegerTranslation()) {
- if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
- aSurfaceOffset = mMatrix.GetTranslation().Round();
- return surface.forget();
- }
+ if (RefPtr<SourceSurface> surface = mInputSurfaces[inputIdx]) {
+ aSurfaceOffset = mMatrix.GetTranslation().Round();
+ aColor = DeviceColor(1, 1, 1, aOptions.mAlpha);
+ return surface.forget();
}
- return FilterNodeWebgl::DrawChild(aDT, aSourceRect, aSurfaceOffset);
+ return FilterNodeWebgl::DrawChild(aParent, aDT, aSourceRect, aOptions,
+ aSurfaceOffset, aColor);
}
return nullptr;
}
@@ -391,8 +488,8 @@ FilterNodeDeferInputWebgl::FilterNodeDeferInputWebgl(
Matrix::Translation(mSourceRect.TopLeft()));
}
-void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT,
- bool aAccel) {
+void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT, bool aAccel,
+ FilterNodeWebgl* aParent) {
uint32_t inputIdx = InputIndex(IN_TRANSFORM_IN);
bool hasAccel = false;
if (inputIdx < NumberOfSetInputs() && mInputSurfaces[inputIdx]) {
@@ -432,10 +529,48 @@ void FilterNodeDeferInputWebgl::ResolveInputs(DrawTargetWebgl* aDT,
}
}
-DeviceColor FilterNodeDeferInputWebgl::GetColor() const {
- return mPattern.GetPattern()->GetType() == PatternType::COLOR
- ? static_cast<const ColorPattern*>(mPattern.GetPattern())->mColor
- : DeviceColor(1, 1, 1, 1);
+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(
@@ -464,35 +599,454 @@ IntRect FilterNodeGaussianBlurWebgl::MapRectToSource(const IntRect& aRect,
void FilterNodeGaussianBlurWebgl::Draw(DrawTargetWebgl* aDT,
const Rect& aSourceRect,
const Point& aDestPoint,
- const DrawOptions& aOptions) {
- ResolveInputs(aDT, true);
+ 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(
- aDT, aSourceRect, surfaceOffset)
+ this, aDT, aSourceRect, DrawOptions(),
+ surfaceOffset, color)
: mInputSurfaces[inputIdx]) {
- DeviceColor color =
- surface->GetFormat() == SurfaceFormat::A8 && mInputFilters[inputIdx]
- ? mInputFilters[inputIdx]->GetColor()
- : DeviceColor(1, 1, 1, 1);
aDT->PushClipRect(Rect(aDestPoint, aSourceRect.Size()));
IntRect surfRect = RoundedOut(
Rect(surface->GetRect()).Intersect(aSourceRect - surfaceOffset));
- Point destOffset =
- Point(surfRect.TopLeft()) + surfaceOffset - aSourceRect.TopLeft();
+ Point destOffset = aDestPoint + Point(surfRect.TopLeft()) +
+ surfaceOffset - aSourceRect.TopLeft();
success = surfRect.IsEmpty() ||
- aDT->BlurSurface(mStdDeviation, surface, surfRect,
- aDestPoint + destOffset, aOptions, color);
+ aDT->BlurSurface(mStdDeviation, surface, surfRect, destOffset,
+ aOptions, color);
aDT->PopClip();
}
if (!success) {
- FilterNodeWebgl::Draw(aDT, aSourceRect, aDestPoint, aOptions);
+ 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 = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_G:
+ mTableG = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_B:
+ mTableB = table;
+ break;
+ case ATT_TABLE_TRANSFER_TABLE_A:
+ mTableA = 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
diff --git a/dom/canvas/FilterNodeWebgl.h b/dom/canvas/FilterNodeWebgl.h
@@ -28,6 +28,8 @@ class FilterNodeWebgl : public FilterNode {
FilterBackend GetBackendType() override { return FILTER_BACKEND_WEBGL; }
+ FilterType GetType() const { return mType; }
+
bool ReserveInputIndex(uint32_t aIndex);
bool SetInputAccel(uint32_t aIndex, SourceSurface* aSurface);
bool SetInputSoftware(uint32_t aIndex, SourceSurface* aSurface);
@@ -46,22 +48,39 @@ class FilterNodeWebgl : public FilterNode {
void SetAttribute(uint32_t aIndex, const Matrix5x4&) override;
void SetAttribute(uint32_t aIndex, const Point3D&) override;
void SetAttribute(uint32_t aIndex, const DeviceColor&) override;
- void SetAttribute(uint32_t aIndex, const Float* aFloat,
+ void SetAttribute(uint32_t aIndex, const Float* aValues,
uint32_t aSize) override;
IntRect MapRectToSource(const IntRect& aRect, const IntRect& aMax,
FilterNode* aSourceNode) override;
+ // Draw the root of a filter chain. Any filter drawing originates here.
virtual void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
- const Point& aDestPoint, const DrawOptions& aOptions);
- virtual already_AddRefed<SourceSurface> DrawChild(DrawTargetWebgl* aDT,
- const Rect& aSourceRect,
- Point& aSurfaceOffset);
- virtual DeviceColor GetColor() const { return DeviceColor(1, 1, 1, 1); }
-
- virtual void ResolveInputs(DrawTargetWebgl* aDT, bool aAccel) {}
-
- void ResolveAllInputs(DrawTargetWebgl* aDT);
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent = nullptr);
+ // Speculatively draw part of a filter chain, only if it can be accelerated.
+ // On success, it return true. Instead of falling back, nothing is drawn and
+ // it returns false.
+ virtual bool DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent = nullptr) {
+ return false;
+ }
+ // Draw a child filter and return a surface that can be processed by a parent
+ // filter. This will try to accelerate the filter if possible, but implements
+ // a fallback if not.
+ virtual already_AddRefed<SourceSurface> DrawChild(
+ FilterNodeWebgl* aParent, DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const DrawOptions& aOptions, Point& aSurfaceOffset, DeviceColor& aColor);
+
+ // This handles deferred filter inputs that should not be resolved to surfaces
+ // until right before drawing. Accelerated filters must ensure they call this
+ // to resolve these inputs before using them for drawing.
+ virtual void ResolveInputs(DrawTargetWebgl* aDT, bool aAccel,
+ FilterNodeWebgl* aParent = nullptr) {}
+
+ // Recursively resolve all inputs in the filter chain.
+ void ResolveAllInputs(DrawTargetWebgl* aDT, FilterNodeWebgl* aParent);
protected:
std::vector<RefPtr<FilterNodeWebgl>> mInputFilters;
@@ -95,7 +114,17 @@ class FilterNodeCropWebgl : public FilterNodeWebgl {
FilterNode* aSourceNode) override;
void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
- const Point& aDestPoint, const DrawOptions& aOptions) override;
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+ bool DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+ already_AddRefed<SourceSurface> DrawChild(FilterNodeWebgl* aParent,
+ DrawTargetWebgl* aDT,
+ const Rect& aSourceRect,
+ const DrawOptions& aOptions,
+ Point& aSurfaceOffset,
+ DeviceColor& aColor) override;
private:
IntRect mCropRect;
@@ -112,9 +141,15 @@ class FilterNodeTransformWebgl : public FilterNodeWebgl {
IntRect MapRectToSource(const IntRect& aRect, const IntRect& aMax,
FilterNode* aSourceNode) override;
- already_AddRefed<SourceSurface> DrawChild(DrawTargetWebgl* aDT,
+ void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+ already_AddRefed<SourceSurface> DrawChild(FilterNodeWebgl* aParent,
+ DrawTargetWebgl* aDT,
const Rect& aSourceRect,
- Point& aSurfaceOffset) override;
+ const DrawOptions& aOptions,
+ Point& aSurfaceOffset,
+ DeviceColor& aColor) override;
protected:
Matrix mMatrix;
@@ -131,9 +166,18 @@ class FilterNodeDeferInputWebgl : public FilterNodeTransformWebgl {
const DrawOptions& aOptions,
const StrokeOptions* aStrokeOptions);
- void ResolveInputs(DrawTargetWebgl* aDT, bool aAccel) override;
+ void ResolveInputs(DrawTargetWebgl* aDT, bool aAccel,
+ FilterNodeWebgl* aParent) override;
- DeviceColor GetColor() const override;
+ void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+ already_AddRefed<SourceSurface> DrawChild(FilterNodeWebgl* aParent,
+ DrawTargetWebgl* aDT,
+ const Rect& aSourceRect,
+ const DrawOptions& aOptions,
+ Point& aSurfaceOffset,
+ DeviceColor& aColor) override;
private:
RefPtr<Path> mPath;
@@ -154,7 +198,8 @@ class FilterNodeGaussianBlurWebgl : public FilterNodeWebgl {
FilterNode* aSourceNode) override;
void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
- const Point& aDestPoint, const DrawOptions& aOptions) override;
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
private:
float mStdDeviation = 0;
@@ -162,6 +207,132 @@ class FilterNodeGaussianBlurWebgl : public FilterNodeWebgl {
int32_t InputIndex(uint32_t aInputEnumIndex) const override;
};
+class FilterNodeColorMatrixWebgl : public FilterNodeWebgl {
+ public:
+ FilterNodeColorMatrixWebgl() : FilterNodeWebgl(FilterType::COLOR_MATRIX) {}
+
+ void SetAttribute(uint32_t aIndex, const Matrix5x4& aValue) override;
+ void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
+
+ bool DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+
+ protected:
+ Matrix5x4 mMatrix;
+ AlphaMode mAlphaMode;
+
+ int32_t InputIndex(uint32_t aInputEnumIndex) const override;
+};
+
+class FilterNodeComponentTransferWebgl : public FilterNodeWebgl {
+ public:
+ explicit FilterNodeComponentTransferWebgl(FilterType aType)
+ : FilterNodeWebgl(aType),
+ mDisableR(true),
+ mDisableG(true),
+ mDisableB(true),
+ mDisableA(true) {}
+
+ void SetAttribute(uint32_t aIndex, bool aValue) override;
+
+ bool DrawAccel(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+
+ virtual bool ToColorMatrix(Matrix5x4& aMatrix) const { return false; }
+
+ protected:
+ bool mDisableR : 1;
+ bool mDisableG : 1;
+ bool mDisableB : 1;
+ bool mDisableA : 1;
+
+ int32_t InputIndex(uint32_t aInputEnumIndex) const override;
+};
+
+class FilterNodeLinearTransferWebgl : public FilterNodeComponentTransferWebgl {
+ public:
+ FilterNodeLinearTransferWebgl()
+ : FilterNodeComponentTransferWebgl(FilterType::LINEAR_TRANSFER) {}
+
+ using FilterNodeComponentTransferWebgl::SetAttribute;
+ void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+ bool ToColorMatrix(Matrix5x4& aMatrix) const override;
+
+ protected:
+ DeviceColor mSlope;
+ DeviceColor mIntercept;
+};
+
+class FilterNodeTableTransferWebgl : public FilterNodeComponentTransferWebgl {
+ public:
+ FilterNodeTableTransferWebgl()
+ : FilterNodeComponentTransferWebgl(FilterType::TABLE_TRANSFER) {}
+
+ using FilterNodeComponentTransferWebgl::SetAttribute;
+ void SetAttribute(uint32_t aIndex, const Float* aValues,
+ uint32_t aSize) override;
+
+ bool ToColorMatrix(Matrix5x4& aMatrix) const override;
+
+ protected:
+ std::vector<Float> mTableR;
+ std::vector<Float> mTableG;
+ std::vector<Float> mTableB;
+ std::vector<Float> mTableA;
+};
+
+class FilterNodePremultiplyWebgl : public FilterNodeWebgl {
+ public:
+ FilterNodePremultiplyWebgl() : FilterNodeWebgl(FilterType::PREMULTIPLY) {}
+
+ void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+
+ protected:
+ int32_t InputIndex(uint32_t aInputEnumIndex) const override;
+};
+
+class FilterNodeUnpremultiplyWebgl : public FilterNodeWebgl {
+ public:
+ FilterNodeUnpremultiplyWebgl() : FilterNodeWebgl(FilterType::UNPREMULTIPLY) {}
+
+ already_AddRefed<SourceSurface> DrawChild(FilterNodeWebgl* aParent,
+ DrawTargetWebgl* aDT,
+ const Rect& aSourceRect,
+ const DrawOptions& aOptions,
+ Point& aSurfaceOffset,
+ DeviceColor& aColor) override;
+
+ protected:
+ int32_t InputIndex(uint32_t aInputEnumIndex) const override;
+};
+
+class FilterNodeOpacityWebgl : public FilterNodeWebgl {
+ public:
+ FilterNodeOpacityWebgl() : FilterNodeWebgl(FilterType::OPACITY) {}
+
+ void SetAttribute(uint32_t aIndex, Float aValue) override;
+
+ void Draw(DrawTargetWebgl* aDT, const Rect& aSourceRect,
+ const Point& aDestPoint, const DrawOptions& aOptions,
+ FilterNodeWebgl* aParent) override;
+ already_AddRefed<SourceSurface> DrawChild(FilterNodeWebgl* aParent,
+ DrawTargetWebgl* aDT,
+ const Rect& aSourceRect,
+ const DrawOptions& aOptions,
+ Point& aSurfaceOffset,
+ DeviceColor& aColor) override;
+
+ protected:
+ Float mValue = 1.0f;
+
+ int32_t InputIndex(uint32_t aInputEnumIndex) const override;
+};
+
} // namespace mozilla::gfx
#endif /* MOZILLA_GFX_FILTERNODEWEBGL_H_ */
diff --git a/gfx/tests/reftest/reftest.list b/gfx/tests/reftest/reftest.list
@@ -44,7 +44,7 @@ skip-if(!cocoaWidget) != 1806140.html 1806140-notref.html
fuzzy(0-1,0-240) == 1812341.html 1812341-ref.html
skip-if(winWidget&&AddressSanitizer) random-if(gtkWidget) fuzzy-if(Android,0-125,0-106) fuzzy-if(winWidget,0-255,0-44) == 1845828-1.html 1845828-1-ref.html # Result on Linux depends on font configuration/hinting/etc, affecting whether subpixel positioning is used
fuzzy-if(!winWidget,0-2,0-106) fuzzy-if(winWidget,0-14,0-314) fuzzy-if(Android&&device,0-3,0-86) == 1853216-1.html 1853216-1-ref.html
-fuzzy-if(winWidget,0-1,0-13) == 1873708-emoji-canvas-filter.html 1873708-emoji-canvas-filter-ref.html
+fuzzy(0-1,0-20) == 1873708-emoji-canvas-filter.html 1873708-emoji-canvas-filter-ref.html
!= 1870240-colrv1-cycle.html 1870240-colrv1-cycle-notref.html
== 1909933.html 1909933-ref.html
fuzzy(0-2,0-6229) random-if(Android) == 1912431-emoji-globalAlpha.html 1912431-emoji-globalAlpha-ref.html