diff --git a/dom/canvas/DrawTargetWebgl.cpp b/dom/canvas/DrawTargetWebgl.cpp index 3835ec2535a5..322446171603 100644 --- a/dom/canvas/DrawTargetWebgl.cpp +++ b/dom/canvas/DrawTargetWebgl.cpp @@ -2598,24 +2598,91 @@ bool SharedContextWebgl::PruneTextureMemory(size_t aMargin, bool aPruneUnused) { return mNumTextureHandles < oldItems; } -void DrawTargetWebgl::FillRect(const Rect& aRect, const Pattern& aPattern, - const DrawOptions& aOptions) { - if (SupportsPattern(aPattern)) { - RectDouble xformRect = TransformDouble(aRect); - if (aPattern.GetType() == PatternType::COLOR) { - if (Maybe clipped = RectClippedToViewport(xformRect)) { - // If the pattern is transform-invariant and the rect clips to the - // viewport, just clip drawing to the viewport to avoid transform - // issues. - DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false); - return; +// Attempt to convert a linear gradient to a 1D ramp texture. +Maybe DrawTargetWebgl::LinearGradientToSurface( + const RectDouble& aBounds, const Pattern& aPattern) { + MOZ_ASSERT(aPattern.GetType() == PatternType::LINEAR_GRADIENT); + const auto& gradient = static_cast(aPattern); + // The gradient points must be transformed by the gradient's matrix. + Point gradBegin = gradient.mMatrix.TransformPoint(gradient.mBegin); + Point gradEnd = gradient.mMatrix.TransformPoint(gradient.mEnd); + // Get the gradient points in user-space. + Point begin = mTransform.TransformPoint(gradBegin); + Point end = mTransform.TransformPoint(gradEnd); + // Find the normalized direction of the gradient and its length. + Point dir = end - begin; + float len = dir.Length(); + dir = dir / len; + // Restrict the rendered bounds to fall within the canvas. + Rect visBounds = NarrowToFloat(aBounds.SafeIntersect(RectDouble(GetRect()))); + // Calculate the distances along the gradient direction of the bounds. + float dist0 = (visBounds.TopLeft() - begin).DotProduct(dir); + float distX = visBounds.width * dir.x; + float distY = visBounds.height * dir.y; + float minDist = floorf( + std::max(dist0 + std::min(distX, 0.0f) + std::min(distY, 0.0f), 0.0f)); + float maxDist = ceilf( + std::min(dist0 + std::max(distX, 0.0f) + std::max(distY, 0.0f), len)); + // Calculate the approximate size of the ramp texture, and see if it would be + // sufficiently smaller than just rendering the primitive. + float subLen = maxDist - minDist; + if (subLen > 0 && subLen < 0.5f * visBounds.Area()) { + // Create a 1D texture to contain the gradient ramp. Reserve two extra + // texels at the beginning and end of the ramp to account for clamping. + RefPtr dt = new DrawTargetSkia; + if (dt->Init(IntSize(int32_t(subLen + 2), 1), SurfaceFormat::B8G8R8A8)) { + // Fill the section of the gradient ramp that is actually used. + dt->FillRect(Rect(dt->GetRect()), + LinearGradientPattern(Point(1 - minDist, 0.0f), + Point(len + 1 - minDist, 0.0f), + gradient.mStops)); + if (RefPtr snapshot = dt->Snapshot()) { + // Calculate a matrix that will map the gradient ramp texture onto the + // actual direction of the gradient. + Point gradDir = (gradEnd - gradBegin) / len; + Point tangent = Point(-gradDir.y, gradDir.x) / gradDir.Length(); + SurfacePattern surfacePattern( + snapshot, ExtendMode::CLAMP, + Matrix(gradDir.x, gradDir.y, tangent.x, tangent.y, gradBegin.x, + gradBegin.y) + .PreTranslate(minDist - 1, 0)); + if (SupportsPattern(surfacePattern)) { + return Some(surfacePattern); + } } } - if (RectInsidePrecisionLimits(xformRect)) { - DrawRect(aRect, aPattern, aOptions); + } + return Nothing(); +} + +void DrawTargetWebgl::FillRect(const Rect& aRect, const Pattern& aPattern, + const DrawOptions& aOptions) { + RectDouble xformRect = TransformDouble(aRect); + if (aPattern.GetType() == PatternType::COLOR) { + if (Maybe clipped = RectClippedToViewport(xformRect)) { + // If the pattern is transform-invariant and the rect clips to the + // viewport, just clip drawing to the viewport to avoid transform + // issues. + DrawRect(*clipped, aPattern, aOptions, Nothing(), nullptr, false); return; } } + if (RectInsidePrecisionLimits(xformRect)) { + if (SupportsPattern(aPattern)) { + DrawRect(aRect, aPattern, aOptions); + return; + } + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { + if (Maybe surface = + LinearGradientToSurface(xformRect, aPattern)) { + if (DrawRect(aRect, *surface, aOptions, Nothing(), nullptr, true, true, + true)) { + return; + } + } + } + } + if (!mWebglValid) { MarkSkiaChanged(aOptions); mSkia->FillRect(aRect, aPattern, aOptions); @@ -2772,7 +2839,7 @@ void DrawTargetWebgl::Fill(const Path* aPath, const Pattern& aPattern, const SkPath& skiaPath = static_cast(aPath)->GetPath(); SkRect skiaRect = SkRect::MakeEmpty(); // Draw the path as a simple rectangle with a supported pattern when possible. - if (skiaPath.isRect(&skiaRect) && SupportsPattern(aPattern)) { + if (skiaPath.isRect(&skiaRect)) { RectDouble rect = SkRectToRectDouble(skiaRect); RectDouble xformRect = TransformDouble(rect); if (aPattern.GetType() == PatternType::COLOR) { @@ -2784,9 +2851,21 @@ void DrawTargetWebgl::Fill(const Path* aPath, const Pattern& aPattern, return; } } + if (RectInsidePrecisionLimits(xformRect)) { - DrawRect(NarrowToFloat(rect), aPattern, aOptions); - return; + if (SupportsPattern(aPattern)) { + DrawRect(NarrowToFloat(rect), aPattern, aOptions); + return; + } + if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { + if (Maybe surface = + LinearGradientToSurface(xformRect, aPattern)) { + if (DrawRect(NarrowToFloat(rect), *surface, aOptions, Nothing(), + nullptr, true, true, true)) { + return; + } + } + } } } diff --git a/dom/canvas/DrawTargetWebgl.h b/dom/canvas/DrawTargetWebgl.h index 6c0e1d4543d1..98a7904967c8 100644 --- a/dom/canvas/DrawTargetWebgl.h +++ b/dom/canvas/DrawTargetWebgl.h @@ -619,6 +619,8 @@ class DrawTargetWebgl : public DrawTarget, public SupportsWeakPtr { bool aTransformed = true, bool aClipped = true, bool aAccelOnly = false, bool aForceUpdate = false, const StrokeOptions* aStrokeOptions = nullptr); + Maybe LinearGradientToSurface(const RectDouble& aBounds, + const Pattern& aPattern); ColorPattern GetClearPattern() const; diff --git a/layout/reftests/canvas/reftest.list b/layout/reftests/canvas/reftest.list index d013124315fc..343cc7dfa3df 100644 --- a/layout/reftests/canvas/reftest.list +++ b/layout/reftests/canvas/reftest.list @@ -103,8 +103,8 @@ fuzzy(0-1,0-43) == 1201272-1.html 1201272-1-ref.html == 1238795-1.html 1238795-1-ref.html == 1303534-1.html 1303534-1-ref.html -fuzzy-if(cocoaWidget,0-1,0-1410) fuzzy-if(winWidget,0-1,0-1410) == 1304353-text-global-alpha-1.html 1304353-text-global-alpha-1-ref.html -fuzzy(0-1,0-1410) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html +fuzzy-if(cocoaWidget,0-1,0-1420) fuzzy-if(winWidget,0-1,0-1420) == 1304353-text-global-alpha-1.html 1304353-text-global-alpha-1-ref.html +fuzzy(0-1,0-1420) == 1304353-text-global-alpha-2.html 1304353-text-global-alpha-2-ref.html fuzzy-if(winWidget,0-94,0-1575) fuzzy-if(cocoaWidget,0-1,0-34) == 1304353-text-global-composite-op-1.html 1304353-text-global-composite-op-1-ref.html == text-indent-1a.html text-indent-1-ref.html diff --git a/layout/reftests/css-gradients/reftest.list b/layout/reftests/css-gradients/reftest.list index 0803f7ec3574..268cc1f2e8f1 100644 --- a/layout/reftests/css-gradients/reftest.list +++ b/layout/reftests/css-gradients/reftest.list @@ -1,5 +1,5 @@ fuzzy(0-4,0-89700) == linear-1a.html linear-1-ref.html -fuzzy(0-2,0-23918) == linear-keywords-1a.html linear-keywords-1-ref.html +fuzzy(0-2,0-27600) == linear-keywords-1a.html linear-keywords-1-ref.html == linear-diagonal-1a.html linear-diagonal-1-ref.html == linear-diagonal-2a.html linear-diagonal-2-ref.html == linear-diagonal-3a.html linear-diagonal-3-ref.html