commit e877ce91550018137b0876cd58ad6b5f4fb08f79
parent 0aab37cd44cf33512ca19dd881dbb688ba7aca84
Author: Lee Salzman <lsalzman@mozilla.com>
Date: Thu, 6 Nov 2025 03:34:44 +0000
Bug 1993150 - Support unbounded blends in AC2D. r=aosmond
Differential Revision: https://phabricator.services.mozilla.com/D271491
Diffstat:
4 files changed, 80 insertions(+), 16 deletions(-)
diff --git a/dom/canvas/DrawTargetWebgl.cpp b/dom/canvas/DrawTargetWebgl.cpp
@@ -600,9 +600,15 @@ void SharedContextWebgl::SetBlendState(CompositionOp aOp,
BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
}
break;
+ case CompositionOp::OP_DEST_OVER:
+ BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_ONE);
+ break;
case CompositionOp::OP_ADD:
BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE);
break;
+ case CompositionOp::OP_DEST_OUT:
+ BlendFunc(LOCAL_GL_ZERO, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
+ break;
case CompositionOp::OP_ATOP:
BlendFunc(LOCAL_GL_DST_ALPHA, LOCAL_GL_ONE_MINUS_SRC_ALPHA);
break;
@@ -645,6 +651,18 @@ void SharedContextWebgl::SetBlendState(CompositionOp aOp,
case CompositionOp::OP_SCREEN:
BlendFunc(LOCAL_GL_ONE, LOCAL_GL_ONE_MINUS_SRC_COLOR);
break;
+ case CompositionOp::OP_IN: // unbounded
+ BlendFunc(LOCAL_GL_DST_ALPHA, LOCAL_GL_ZERO);
+ break;
+ case CompositionOp::OP_OUT: // unbounded
+ BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_ZERO);
+ break;
+ case CompositionOp::OP_DEST_IN: // unbounded
+ BlendFunc(LOCAL_GL_ZERO, LOCAL_GL_SRC_ALPHA);
+ break;
+ case CompositionOp::OP_DEST_ATOP: // unbounded
+ BlendFunc(LOCAL_GL_ONE_MINUS_DST_ALPHA, LOCAL_GL_SRC_ALPHA);
+ break;
default:
enabled = false;
break;
@@ -2120,17 +2138,52 @@ bool DrawTargetWebgl::CopyToFallback(DrawTarget* aDT) {
return false;
}
+enum class SupportsDrawOptionsStatus { No, UnboundedBlend, Yes };
+
// Whether a given composition operator can be mapped to a WebGL blend mode.
-static inline bool SupportsDrawOptions(const DrawOptions& aOptions) {
+static inline SupportsDrawOptionsStatus SupportsDrawOptions(
+ const DrawOptions& aOptions) {
switch (aOptions.mCompositionOp) {
case CompositionOp::OP_OVER:
+ case CompositionOp::OP_DEST_OVER:
case CompositionOp::OP_ADD:
+ case CompositionOp::OP_DEST_OUT:
case CompositionOp::OP_ATOP:
case CompositionOp::OP_SOURCE:
case CompositionOp::OP_CLEAR:
case CompositionOp::OP_MULTIPLY:
case CompositionOp::OP_SCREEN:
+ return SupportsDrawOptionsStatus::Yes;
+ case CompositionOp::OP_IN:
+ case CompositionOp::OP_OUT:
+ case CompositionOp::OP_DEST_IN:
+ case CompositionOp::OP_DEST_ATOP:
+ return SupportsDrawOptionsStatus::UnboundedBlend;
+ default:
+ return SupportsDrawOptionsStatus::No;
+ }
+}
+
+bool DrawTargetWebgl::SupportsDrawOptions(const DrawOptions& aOptions,
+ const Rect& aRect) {
+ switch (mozilla::gfx::SupportsDrawOptions(aOptions)) {
+ case SupportsDrawOptionsStatus::Yes:
return true;
+ case SupportsDrawOptionsStatus::UnboundedBlend:
+ if (aRect.IsEmpty()) {
+ return false;
+ }
+ if (Maybe<IntRect> clip = mSkia->GetDeviceClipRect(false)) {
+ if (!clip->IsEmpty() && clip->Contains(GetRect())) {
+ clip = Some(GetRect());
+ }
+ Rect clipF(*clip);
+ if (aRect.Contains(clipF) || aRect.WithinEpsilonOf(clipF, 1e-3f)) {
+ return true;
+ }
+ return false;
+ }
+ return false;
default:
return false;
}
@@ -2218,7 +2271,7 @@ bool DrawTargetWebgl::DrawRect(const Rect& aRect, const Pattern& aPattern,
bool aAccelOnly, bool aForceUpdate,
const StrokeOptions* aStrokeOptions) {
// If there is nothing to draw, then don't draw...
- if (aRect.IsEmpty()) {
+ if (aRect.IsEmpty() || mSkia->IsClipEmpty()) {
return true;
}
@@ -2236,7 +2289,7 @@ bool DrawTargetWebgl::DrawRect(const Rect& aRect, const Pattern& aPattern,
// or we are going to flush the Skia layer to it before doing so. The shared
// context still needs to be claimed and prepared for drawing. If this
// fails, we just fall back to drawing with Skia below.
- if (PrepareContext(aClipped)) {
+ if (SupportsDrawOptions(aOptions, aRect) && PrepareContext(aClipped)) {
// The shared context is claimed and the framebuffer is now valid, so try
// accelerated drawing.
return mSharedContext->DrawRectAccel(
@@ -2737,7 +2790,7 @@ bool SharedContextWebgl::DrawRectAccel(
// the Skia target. When we need to forcefully update a texture, we must be
// careful to override any pattern limits, as the caller ensures the pattern
// is otherwise a supported type.
- if (!SupportsDrawOptions(aOptions) ||
+ if (SupportsDrawOptions(aOptions) == SupportsDrawOptionsStatus::No ||
(!aForceUpdate && !SupportsPattern(aPattern)) || aStrokeOptions ||
(!mTargetHandle && !mCurrentTarget->MarkChanged())) {
// If only accelerated drawing was requested, bail out without software
@@ -3444,9 +3497,10 @@ bool DrawTargetWebgl::FilterSurface(const Matrix5x4& aColorMatrix,
const IntRect& aSourceRect,
const Point& aDest,
const DrawOptions& aOptions) {
- if (ShouldAccelPath(aOptions, nullptr)) {
- IntRect sourceRect =
- aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
+ IntRect sourceRect =
+ aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
+ if (ShouldAccelPath(aOptions, nullptr,
+ Rect(aDest, Size(sourceRect.Size())))) {
if (mTransform.IsTranslation() &&
!mSharedContext->RequiresMultiStageBlend(aOptions, this)) {
// If the transform doesn't require resampling and the blend op is simple,
@@ -3797,12 +3851,13 @@ bool DrawTargetWebgl::BlurSurface(float aSigma, SourceSurface* aSurface,
const Point& aDest,
const DrawOptions& aOptions,
const DeviceColor& aColor) {
- Maybe<DeviceColor> maskColor =
- aSurface->GetFormat() == SurfaceFormat::A8 ? Some(aColor) : Nothing();
+ IntRect sourceRect =
+ aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
if (aSigma >= 0.0f && aSigma <= BLUR_ACCEL_SIGMA_MAX &&
- ShouldAccelPath(aOptions, nullptr)) {
- IntRect sourceRect =
- aSourceRect.IsEmpty() ? aSurface->GetRect() : aSourceRect;
+ ShouldAccelPath(aOptions, nullptr,
+ Rect(aDest, Size(sourceRect.Size())))) {
+ Maybe<DeviceColor> maskColor =
+ aSurface->GetFormat() == SurfaceFormat::A8 ? Some(aColor) : Nothing();
if (aSigma < BLUR_ACCEL_SIGMA_MIN) {
SurfacePattern maskPattern(aSurface, ExtendMode::CLAMP,
Matrix::Translation(aDest));
@@ -3886,7 +3941,7 @@ already_AddRefed<TextureHandle> SharedContextWebgl::ResolveFilterInputAccel(
const IntRect& aSourceRect, const Matrix& aDestTransform,
const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions,
SurfaceFormat aFormat) {
- if (!SupportsDrawOptions(aOptions)) {
+ if (SupportsDrawOptions(aOptions) != SupportsDrawOptionsStatus::Yes) {
return nullptr;
}
if (IsContextLost()) {
@@ -4644,8 +4699,10 @@ void PathCache::ClearVertexRanges() {
}
inline bool DrawTargetWebgl::ShouldAccelPath(
- const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions) {
- return mWebglValid && SupportsDrawOptions(aOptions) && PrepareContext();
+ const DrawOptions& aOptions, const StrokeOptions* aStrokeOptions,
+ const Rect& aRect) {
+ return mWebglValid && SupportsDrawOptions(aOptions, aRect) &&
+ PrepareContext();
}
// For now, we only directly support stroking solid color patterns to limit
diff --git a/dom/canvas/DrawTargetWebgl.h b/dom/canvas/DrawTargetWebgl.h
@@ -755,6 +755,9 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
return mSharedContext->SupportsPattern(aPattern);
}
+ bool SupportsDrawOptions(const DrawOptions& aOptions,
+ const Rect& aRect = Rect());
+
bool SetSimpleClipRect();
bool GenerateComplexClipMask();
bool PrepareContext(bool aClipped = true,
@@ -784,7 +787,8 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr {
Maybe<Rect> RectClippedToViewport(const RectDouble& aRect) const;
bool ShouldAccelPath(const DrawOptions& aOptions,
- const StrokeOptions* aStrokeOptions);
+ const StrokeOptions* aStrokeOptions,
+ const Rect& aRect = Rect());
void DrawPath(const Path* aPath, const Pattern& aPattern,
const DrawOptions& aOptions,
const StrokeOptions* aStrokeOptions = nullptr,
diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp
@@ -2033,6 +2033,8 @@ Maybe<IntRect> DrawTargetSkia::GetDeviceClipRect(bool aAllowComplex) const {
return Nothing();
}
+bool DrawTargetSkia::IsClipEmpty() const { return mCanvas->isClipEmpty(); }
+
void DrawTargetSkia::PushLayer(bool aOpaque, Float aOpacity,
SourceSurface* aMask,
const Matrix& aMaskTransform,
diff --git a/gfx/2d/DrawTargetSkia.h b/gfx/2d/DrawTargetSkia.h
@@ -159,6 +159,7 @@ class DrawTargetSkia : public DrawTarget {
}
Maybe<IntRect> GetDeviceClipRect(bool aAllowComplex = false) const;
+ bool IsClipEmpty() const;
Maybe<Rect> GetGlyphLocalBounds(ScaledFont* aFont, const GlyphBuffer& aBuffer,
const Pattern& aPattern,