diff options
author | Moonchild <moonchild@palemoon.org> | 2023-10-30 12:23:36 +0100 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2023-11-01 10:31:16 +0100 |
commit | 5fd365cb76597c272356810f36efba33d8b013fe (patch) | |
tree | b3556bc4f7903d0cf17d1d1d46430a220306fe17 | |
parent | d7f8e56f04f1684d0c9ffca7ff4cf7cea2642aa9 (diff) | |
download | uxp-5fd365cb76597c272356810f36efba33d8b013fe.tar.gz |
Issue #2364 - Re-factor AlphaBoxBlur
Only blur one quadrant of a box-shadow and mirror it to the other quadrants.
This applies only if the corners are symmetrical (square corners or equal
corner radii) otherwise we'll fall back to the old method.
-rw-r--r-- | gfx/2d/Blur.cpp | 33 | ||||
-rw-r--r-- | gfx/2d/Blur.h | 8 | ||||
-rw-r--r-- | gfx/2d/PathHelpers.h | 9 | ||||
-rw-r--r-- | gfx/thebes/gfxBlur.cpp | 781 | ||||
-rw-r--r-- | gfx/thebes/gfxBlur.h | 68 | ||||
-rw-r--r-- | layout/base/nsCSSRendering.cpp | 8 |
6 files changed, 556 insertions, 351 deletions
diff --git a/gfx/2d/Blur.cpp b/gfx/2d/Blur.cpp index 6a38b66669..143a39e9ad 100644 --- a/gfx/2d/Blur.cpp +++ b/gfx/2d/Blur.cpp @@ -335,10 +335,26 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, const IntSize& aBlurRadius, const Rect* aDirtyRect, const Rect* aSkipRect) - : mSpreadRadius(aSpreadRadius), - mBlurRadius(aBlurRadius), - mSurfaceAllocationSize(0) + : mSurfaceAllocationSize(0) { + Init(aRect, aSpreadRadius, aBlurRadius, aDirtyRect, aSkipRect); +} + +AlphaBoxBlur::AlphaBoxBlur() + : mSurfaceAllocationSize(0) +{ +} + +void +AlphaBoxBlur::Init(const Rect& aRect, + const IntSize& aSpreadRadius, + const IntSize& aBlurRadius, + const Rect* aDirtyRect, + const Rect* aSkipRect) +{ + mSpreadRadius = aSpreadRadius; + mBlurRadius = aBlurRadius; + Rect rect(aRect); rect.Inflate(Size(aBlurRadius + aSpreadRadius)); rect.RoundOut(); @@ -355,8 +371,7 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, mHasDirtyRect = false; } - mRect = IntRect(int32_t(rect.x), int32_t(rect.y), - int32_t(rect.width), int32_t(rect.height)); + mRect = TruncatedToInt(rect); if (mRect.IsEmpty()) { return; } @@ -366,11 +381,8 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, // blurring/spreading we need to do. We convert it to IntRect to avoid // expensive int<->float conversions if we were to use Rect instead. Rect skipRect = *aSkipRect; - skipRect.RoundIn(); skipRect.Deflate(Size(aBlurRadius + aSpreadRadius)); - mSkipRect = IntRect(int32_t(skipRect.x), int32_t(skipRect.y), - int32_t(skipRect.width), int32_t(skipRect.height)); - + mSkipRect = RoundedIn(skipRect); mSkipRect = mSkipRect.Intersect(mRect); if (mSkipRect.IsEqualInterior(mRect)) return; @@ -397,8 +409,7 @@ AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect, int32_t aStride, float aSigmaX, float aSigmaY) - : mRect(int32_t(aRect.x), int32_t(aRect.y), - int32_t(aRect.width), int32_t(aRect.height)), + : mRect(TruncatedToInt(aRect)), mSpreadRadius(), mBlurRadius(CalculateBlurRadius(Point(aSigmaX, aSigmaY))), mStride(aStride), diff --git a/gfx/2d/Blur.h b/gfx/2d/Blur.h index 6fffeb2390..d4a9af3e36 100644 --- a/gfx/2d/Blur.h +++ b/gfx/2d/Blur.h @@ -68,6 +68,14 @@ public: float aSigmaX, float aSigmaY); + AlphaBoxBlur(); + + void Init(const Rect& aRect, + const IntSize& aSpreadRadius, + const IntSize& aBlurRadius, + const Rect* aDirtyRect, + const Rect* aSkipRect); + ~AlphaBoxBlur(); /** diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index 553a886b93..6b7135db1b 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -224,6 +224,15 @@ struct RectCornerRadii { return true; } + bool AreRadiiSame() const { + for (size_t i = 1; i < RectCorner::Count; i++) { + if (radii[i] != radii[0]) { + return false; + } + } + return true; + } + void Scale(Float aXScale, Float aYScale) { for (int i = 0; i < RectCorner::Count; i++) { radii[i].Scale(aXScale, aYScale); diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index d5878e1619..479a1b36d0 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -11,8 +11,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/gfx/Blur.h" #include "mozilla/gfx/PathHelpers.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/UniquePtrExtensions.h" +#include "mozilla/Maybe.h" #include "nsExpirationTracker.h" #include "nsClassHashtable.h" #include "gfxUtils.h" @@ -21,71 +20,69 @@ using namespace mozilla; using namespace mozilla::gfx; gfxAlphaBoxBlur::gfxAlphaBoxBlur() + : mData(nullptr) { } gfxAlphaBoxBlur::~gfxAlphaBoxBlur() { - mContext = nullptr; + if (mData) { + free(mData); + } } -gfxContext* +already_AddRefed<gfxContext> gfxAlphaBoxBlur::Init(const gfxRect& aRect, const IntSize& aSpreadRadius, const IntSize& aBlurRadius, const gfxRect* aDirtyRect, const gfxRect* aSkipRect) { - mozilla::gfx::Rect rect(Float(aRect.x), Float(aRect.y), - Float(aRect.width), Float(aRect.height)); - IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height); - IntSize blurRadius(aBlurRadius.width, aBlurRadius.height); - UniquePtr<Rect> dirtyRect; - if (aDirtyRect) { - dirtyRect = MakeUnique<Rect>(Float(aDirtyRect->x), - Float(aDirtyRect->y), - Float(aDirtyRect->width), - Float(aDirtyRect->height)); - } - UniquePtr<Rect> skipRect; - if (aSkipRect) { - skipRect = MakeUnique<Rect>(Float(aSkipRect->x), - Float(aSkipRect->y), - Float(aSkipRect->width), - Float(aSkipRect->height)); - } - - mBlur = MakeUnique<AlphaBoxBlur>(rect, spreadRadius, blurRadius, dirtyRect.get(), skipRect.get()); - size_t blurDataSize = mBlur->GetSurfaceAllocationSize(); - if (blurDataSize == 0) - return nullptr; - - IntSize size = mBlur->GetSize(); + Maybe<Rect> dirtyRect = aDirtyRect ? Some(ToRect(*aDirtyRect)) : Nothing(); + Maybe<Rect> skipRect = aSkipRect ? Some(ToRect(*aSkipRect)) : Nothing(); + RefPtr<DrawTarget> dt = + InitDrawTarget(ToRect(aRect), aSpreadRadius, aBlurRadius, + dirtyRect.ptrOr(nullptr), skipRect.ptrOr(nullptr)); + if (!dt) { + return nullptr; + } - // Make an alpha-only surface to draw on. We will play with the data after - // everything is drawn to create a blur effect. - mData = MakeUniqueFallible<unsigned char[]>(blurDataSize); - if (!mData) { - return nullptr; - } - memset(mData.get(), 0, blurDataSize); - - RefPtr<DrawTarget> dt = - gfxPlatform::CreateDrawTargetForData(mData.get(), size, - mBlur->GetStride(), - SurfaceFormat::A8); - if (!dt || !dt->IsValid()) { - return nullptr; - } + RefPtr<gfxContext> context = gfxContext::CreateOrNull(dt); + MOZ_ASSERT(context); // already checked for target above + context->SetMatrix(gfxMatrix::Translation(-mBlur.GetRect().TopLeft())); + return context.forget(); +} - IntRect irect = mBlur->GetRect(); - gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y); +already_AddRefed<DrawTarget> +gfxAlphaBoxBlur::InitDrawTarget(const Rect& aRect, + const IntSize& aSpreadRadius, + const IntSize& aBlurRadius, + const Rect* aDirtyRect, + const Rect* aSkipRect) +{ + mBlur.Init(aRect, aSpreadRadius, aBlurRadius, aDirtyRect, aSkipRect); + size_t blurDataSize = mBlur.GetSurfaceAllocationSize(); + if (blurDataSize == 0) { + return nullptr; + } - mContext = gfxContext::CreateOrNull(dt); - MOZ_ASSERT(mContext); // already checked for target above - mContext->SetMatrix(gfxMatrix::Translation(-topleft)); + // Make an alpha-only surface to draw on. We will play with the data after + // everything is drawn to create a blur effect. + mData = static_cast<uint8_t*>(calloc(1, blurDataSize)); + if (!mData) { + return nullptr; + } - return mContext; + RefPtr<DrawTarget> dt = + gfxPlatform::CreateDrawTargetForData(mData, + mBlur.GetSize(), + mBlur.GetStride(), + SurfaceFormat::A8); + if (!dt || !dt->IsValid()) { + return nullptr; + } + dt->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft())); + return dt.forget(); } void @@ -121,20 +118,20 @@ DrawBlur(gfxContext* aDestinationCtx, already_AddRefed<SourceSurface> gfxAlphaBoxBlur::DoBlur(DrawTarget* aDT, IntPoint* aTopLeft) { - mBlur->Blur(mData.get()); + mBlur.Blur(mData); - *aTopLeft = mBlur->GetRect().TopLeft(); + *aTopLeft = mBlur.GetRect().TopLeft(); - return aDT->CreateSourceSurfaceFromData(mData.get(), - mBlur->GetSize(), - mBlur->GetStride(), + return aDT->CreateSourceSurfaceFromData(mData, + mBlur.GetSize(), + mBlur.GetStride(), SurfaceFormat::A8); } void gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx) { - if (!mContext) + if (!mData) return; DrawTarget *dest = aDestinationCtx->GetDrawTarget(); @@ -143,7 +140,7 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx) return; } - Rect* dirtyRect = mBlur->GetDirtyRect(); + Rect* dirtyRect = mBlur.GetDirtyRect(); IntPoint topLeft; RefPtr<SourceSurface> mask = DoBlur(dest, &topLeft); @@ -175,16 +172,15 @@ struct BlurCacheKey : public PLDHashEntryHdr { bool mIsInset; // Only used for inset blurs - bool mHasBorderRadius; IntSize mInnerMinSize; - BlurCacheKey(IntSize aMinSize, IntSize aBlurRadius, - RectCornerRadii* aCornerRadii, const Color& aShadowColor, + BlurCacheKey(const IntSize& aMinSize, const IntSize& aBlurRadius, + const RectCornerRadii* aCornerRadii, const Color& aShadowColor, BackendType aBackendType) : BlurCacheKey(aMinSize, IntSize(0, 0), aBlurRadius, aCornerRadii, aShadowColor, false, - false, aBackendType) + aBackendType) {} explicit BlurCacheKey(const BlurCacheKey* aOther) @@ -194,22 +190,20 @@ struct BlurCacheKey : public PLDHashEntryHdr { , mBackend(aOther->mBackend) , mCornerRadii(aOther->mCornerRadii) , mIsInset(aOther->mIsInset) - , mHasBorderRadius(aOther->mHasBorderRadius) , mInnerMinSize(aOther->mInnerMinSize) { } - explicit BlurCacheKey(IntSize aOuterMinSize, IntSize aInnerMinSize, - IntSize aBlurRadius, + explicit BlurCacheKey(const IntSize& aOuterMinSize, const IntSize& aInnerMinSize, + const IntSize& aBlurRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, bool aIsInset, - bool aHasBorderRadius, BackendType aBackendType) + BackendType aBackendType) : mMinSize(aOuterMinSize) , mBlurRadius(aBlurRadius) , mShadowColor(aShadowColor) , mBackend(aBackendType) , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii()) , mIsInset(aIsInset) - , mHasBorderRadius(aHasBorderRadius) , mInnerMinSize(aInnerMinSize) { } @@ -237,7 +231,6 @@ struct BlurCacheKey : public PLDHashEntryHdr { if (aKey->mIsInset) { hash = AddToHash(hash, aKey->mInnerMinSize.width, aKey->mInnerMinSize.height); - hash = AddToHash(hash, HashBytes(&aKey->mHasBorderRadius, sizeof(bool))); } return hash; } @@ -252,8 +245,7 @@ struct BlurCacheKey : public PLDHashEntryHdr { aKey->mBackend == mBackend) { if (mIsInset) { - return (mHasBorderRadius == aKey->mHasBorderRadius) && - (mInnerMinSize == aKey->mInnerMinSize); + return (mInnerMinSize == aKey->mInnerMinSize); } return true; @@ -274,15 +266,15 @@ struct BlurCacheKey : public PLDHashEntryHdr { * to the cache entry to be able to be tracked by the nsExpirationTracker. * */ struct BlurCacheData { - BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey) + BlurCacheData(SourceSurface* aBlur, const IntMargin& aBlurMargin, const BlurCacheKey& aKey) : mBlur(aBlur) - , mExtendDest(aExtendDestBy) + , mBlurMargin(aBlurMargin) , mKey(aKey) {} BlurCacheData(const BlurCacheData& aOther) : mBlur(aOther.mBlur) - , mExtendDest(aOther.mExtendDest) + , mBlurMargin(aOther.mBlurMargin) , mKey(aOther.mKey) { } @@ -292,7 +284,7 @@ struct BlurCacheData { nsExpirationState mExpirationState; RefPtr<SourceSurface> mBlur; - IntMargin mExtendDest; + IntMargin mBlurMargin; BlurCacheKey mKey; }; @@ -316,9 +308,9 @@ class BlurCache final : public nsExpirationTracker<BlurCacheData,4> mHashEntries.Remove(aObject->mKey); } - BlurCacheData* Lookup(const IntSize aMinSize, + BlurCacheData* Lookup(const IntSize& aMinSize, const IntSize& aBlurRadius, - RectCornerRadii* aCornerRadii, + const RectCornerRadii* aCornerRadii, const Color& aShadowColor, BackendType aBackendType) { @@ -333,19 +325,18 @@ class BlurCache final : public nsExpirationTracker<BlurCacheData,4> return blur; } - BlurCacheData* LookupInsetBoxShadow(const IntSize aOuterMinSize, - const IntSize aInnerMinSize, + BlurCacheData* LookupInsetBoxShadow(const IntSize& aOuterMinSize, + const IntSize& aInnerMinSize, const IntSize& aBlurRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, - const bool& aHasBorderRadius, BackendType aBackendType) { bool insetBoxShadow = true; BlurCacheKey key(aOuterMinSize, aInnerMinSize, aBlurRadius, aCornerRadii, aShadowColor, insetBoxShadow, - aHasBorderRadius, aBackendType); + aBackendType); BlurCacheData* blur = mHashEntries.Get(key); if (blur) { MarkUsed(blur); @@ -383,28 +374,26 @@ class BlurCache final : public nsExpirationTracker<BlurCacheData,4> static BlurCache* gBlurCache = nullptr; static IntSize -ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii, - IntSize aBlurRadius, - IntMargin& aSlice, +ComputeMinSizeForShadowShape(const RectCornerRadii* aCornerRadii, + const IntSize& aBlurRadius, + IntMargin& aOutSlice, const IntSize& aRectSize) { - float cornerWidth = 0; - float cornerHeight = 0; + Size cornerSize(0, 0); if (aCornerRadii) { - RectCornerRadii corners = *aCornerRadii; - for (size_t i = 0; i < 4; i++) { - cornerWidth = std::max(cornerWidth, corners[i].width); - cornerHeight = std::max(cornerHeight, corners[i].height); + const RectCornerRadii& corners = *aCornerRadii; + for (size_t i = 0; i < RectCorner::Count; i++) { + cornerSize.width = std::max(cornerSize.width, corners[i].width); + cornerSize.height = std::max(cornerSize.height, corners[i].height); } } - aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height, - ceil(cornerWidth) + aBlurRadius.width, - ceil(cornerHeight) + aBlurRadius.height, - ceil(cornerWidth) + aBlurRadius.width); + IntSize margin = IntSize::Ceil(cornerSize) + aBlurRadius; + aOutSlice = IntMargin(margin.height, margin.width, + margin.height, margin.width); - IntSize minSize(aSlice.LeftRight() + 1, - aSlice.TopBottom() + 1); + IntSize minSize(aOutSlice.LeftRight() + 1, + aOutSlice.TopBottom() + 1); // If aRectSize is smaller than minSize, the border-image approach won't // work; there's no way to squeeze parts of the min box-shadow source @@ -414,31 +403,31 @@ ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii, // to slice away more than we have. if (aRectSize.width < minSize.width) { minSize.width = aRectSize.width; - aSlice.left = 0; - aSlice.right = 0; + aOutSlice.left = 0; + aOutSlice.right = 0; } if (aRectSize.height < minSize.height) { minSize.height = aRectSize.height; - aSlice.top = 0; - aSlice.bottom = 0; + aOutSlice.top = 0; + aOutSlice.bottom = 0; } - MOZ_ASSERT(aSlice.LeftRight() <= minSize.width); - MOZ_ASSERT(aSlice.TopBottom() <= minSize.height); + MOZ_ASSERT(aOutSlice.LeftRight() <= minSize.width); + MOZ_ASSERT(aOutSlice.TopBottom() <= minSize.height); return minSize; } void -CacheBlur(DrawTarget& aDT, +CacheBlur(DrawTarget* aDT, const IntSize& aMinSize, const IntSize& aBlurRadius, - RectCornerRadii* aCornerRadii, + const RectCornerRadii* aCornerRadii, const Color& aShadowColor, - IntMargin aExtendDest, + const IntMargin& aBlurMargin, SourceSurface* aBoxShadow) { - BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType()); - BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key); + BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT->GetBackendType()); + BlurCacheData* data = new BlurCacheData(aBoxShadow, aBlurMargin, key); if (!gBlurCache->RegisterEntry(data)) { delete data; } @@ -446,56 +435,59 @@ CacheBlur(DrawTarget& aDT, // Blurs a small surface and creates the mask. static already_AddRefed<SourceSurface> -CreateBlurMask(const IntSize& aMinSize, - RectCornerRadii* aCornerRadii, - IntSize aBlurRadius, - IntMargin& aExtendDestBy, - IntMargin& aSliceBorder, - DrawTarget& aDestDrawTarget) +CreateBlurMask(DrawTarget* aDestDrawTarget, + const IntSize& aMinSize, + const RectCornerRadii* aCornerRadii, + const IntSize& aBlurRadius, + bool aMirrorCorners, + IntMargin& aOutBlurMargin) { gfxAlphaBoxBlur blur; - IntRect minRect(IntPoint(), aMinSize); - - gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), IntSize(), - aBlurRadius, nullptr, nullptr); - - if (!blurCtx) { + Rect minRect(Point(0, 0), Size(aMinSize)); + Rect blurRect(minRect); + // If mirroring corners, we only need to draw the top-left quadrant. + // Use ceil to preserve the remaining 1x1 middle area for minimized box + // shadows. + if (aMirrorCorners) { + blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f)); + } + IntSize zeroSpread(0, 0); + RefPtr<DrawTarget> blurDT = + blur.InitDrawTarget(blurRect, zeroSpread, aBlurRadius); + if (!blurDT) { return nullptr; } - DrawTarget* blurDT = blurCtx->GetDrawTarget(); ColorPattern black(Color(0.f, 0.f, 0.f, 1.f)); if (aCornerRadii) { RefPtr<Path> roundedRect = - MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii); + MakePathForRoundedRect(*blurDT, minRect, *aCornerRadii); blurDT->Fill(roundedRect, black); } else { - blurDT->FillRect(Rect(minRect), black); + blurDT->FillRect(minRect, black); } IntPoint topLeft; - RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft); + RefPtr<SourceSurface> result = blur.DoBlur(aDestDrawTarget, &topLeft); if (!result) { return nullptr; } - IntRect expandedMinRect(topLeft, result->GetSize()); - aExtendDestBy = expandedMinRect - minRect; - aSliceBorder += aExtendDestBy; - - MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width); - MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height); + // Since blurRect is at (0, 0), we can find the inflated margin by + // negating the new rect origin, which would have been negative if + // the rect was inflated. + aOutBlurMargin = IntMargin(-topLeft.y, -topLeft.x, -topLeft.y, -topLeft.x); return result.forget(); } static already_AddRefed<SourceSurface> -CreateBoxShadow(DrawTarget& aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor) +CreateBoxShadow(DrawTarget* aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor) { IntSize blurredSize = aBlurMask->GetSize(); RefPtr<DrawTarget> boxShadowDT = - Factory::CreateDrawTarget(aDestDT.GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8); + Factory::CreateDrawTarget(aDestDT->GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8); if (!boxShadowDT) { return nullptr; @@ -510,17 +502,19 @@ static already_AddRefed<SourceSurface> GetBlur(gfxContext* aDestinationCtx, const IntSize& aRectSize, const IntSize& aBlurRadius, - RectCornerRadii* aCornerRadii, + const RectCornerRadii* aCornerRadii, const Color& aShadowColor, - IntMargin& aExtendDestBy, - IntMargin& aSlice) + bool aMirrorCorners, + IntMargin& aOutBlurMargin, + IntMargin& aOutSlice, + IntSize& aOutMinSize) { if (!gBlurCache) { gBlurCache = new BlurCache(); } IntSize minSize = - ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize); + ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aOutSlice, aRectSize); // We can get seams using the min size rect when drawing to the destination rect // if we have a non-pixel aligned destination transformation. In those cases, @@ -530,24 +524,25 @@ GetBlur(gfxContext* aDestinationCtx, if (useDestRect) { minSize = aRectSize; } - - DrawTarget& destDT = *aDestinationCtx->GetDrawTarget(); - - BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius, - aCornerRadii, aShadowColor, - destDT.GetBackendType()); - if (cached && !useDestRect) { - // See CreateBlurMask() for these values - aExtendDestBy = cached->mExtendDest; - aSlice = aSlice + aExtendDestBy; - RefPtr<SourceSurface> blur = cached->mBlur; - return blur.forget(); + aOutMinSize = minSize; + + DrawTarget* destDT = aDestinationCtx->GetDrawTarget(); + + if (!useDestRect) { + BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius, + aCornerRadii, aShadowColor, + destDT->GetBackendType()); + if (cached) { + // See CreateBlurMask() for these values + aOutBlurMargin = cached->mBlurMargin; + RefPtr<SourceSurface> blur = cached->mBlur; + return blur.forget(); + } } RefPtr<SourceSurface> blurMask = - CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, - destDT); - + CreateBlurMask(destDT, minSize, aCornerRadii, aBlurRadius, + aMirrorCorners, aOutBlurMargin); if (!blurMask) { return nullptr; } @@ -557,12 +552,9 @@ GetBlur(gfxContext* aDestinationCtx, return nullptr; } - if (useDestRect) { - // Since we're just going to paint the actual rect to the destination - aSlice.SizeTo(0, 0, 0, 0); - } else { + if (!useDestRect) { CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, - aExtendDestBy, boxShadow); + aOutBlurMargin, boxShadow); } return boxShadow.forget(); } @@ -580,49 +572,56 @@ RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft) return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop); } +static bool +ShouldStretchSurface(DrawTarget* aDT, SourceSurface* aSurface) +{ + // Use stretching if possible, since it leads to less seams when the + // destination is transformed. However, don't do this if we're using cairo, + // because if cairo is using pixman it won't render anything for large + // stretch factors because pixman's internal fixed point precision is not + // high enough to handle those scale factors. + // Calling FillRect on a D2D backend with a repeating pattern is much slower + // than DrawSurface, so special case the D2D backend here. + return (!aDT->GetTransform().IsRectilinear() && + aDT->GetBackendType() != BackendType::CAIRO) || + (aDT->GetBackendType() == BackendType::DIRECT2D1_1); +} + static void -RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, - const Rect& aDest, const Rect& aSrc, Rect& aSkipRect) +RepeatOrStretchSurface(DrawTarget* aDT, SourceSurface* aSurface, + const Rect& aDest, const Rect& aSrc, const Rect& aSkipRect) { if (aSkipRect.Contains(aDest)) { return; } - if ((!aDT.GetTransform().IsRectilinear() && - aDT.GetBackendType() != BackendType::CAIRO) || - (aDT.GetBackendType() == BackendType::DIRECT2D1_1)) { - // Use stretching if possible, since it leads to less seams when the - // destination is transformed. However, don't do this if we're using cairo, - // because if cairo is using pixman it won't render anything for large - // stretch factors because pixman's internal fixed point precision is not - // high enough to handle those scale factors. - // Calling FillRect on a D2D backend with a repeating pattern is much slower - // than DrawSurface, so special case the D2D backend here. - aDT.DrawSurface(aSurface, aDest, aSrc); + if (ShouldStretchSurface(aDT, aSurface)) { + aDT->DrawSurface(aSurface, aDest, aSrc); return; } SurfacePattern pattern(aSurface, ExtendMode::REPEAT, Matrix::Translation(aDest.TopLeft() - aSrc.TopLeft()), SamplingFilter::GOOD, RoundedToInt(aSrc)); - aDT.FillRect(aDest, pattern); + aDT->FillRect(aDest, pattern); } static void -DrawCorner(DrawTarget& aDT, SourceSurface* aSurface, - const Rect& aDest, const Rect& aSrc, Rect& aSkipRect) +DrawCorner(DrawTarget* aDT, SourceSurface* aSurface, + const Rect& aDest, const Rect& aSrc, const Rect& aSkipRect) { if (aSkipRect.Contains(aDest)) { return; } - aDT.DrawSurface(aSurface, aDest, aSrc); + aDT->DrawSurface(aSurface, aDest, aSrc); } static void -DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur, - Rect aDstOuter, Rect aDstInner, Rect aSrcOuter, Rect aSrcInner, - Rect aSkipRect) +DrawMinBoxShadow(DrawTarget* aDestDrawTarget, SourceSurface* aSourceBlur, + const Rect& aDstOuter, const Rect& aDstInner, + const Rect& aSrcOuter, const Rect& aSrcInner, + const Rect& aSkipRect, bool aMiddle = false) { // Corners: top left, top right, bottom left, bottom right DrawCorner(aDestDrawTarget, aSourceBlur, @@ -679,6 +678,192 @@ DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur, RectWithEdgesTRBL(aSrcInner.YMost(), aSrcInner.XMost(), aSrcOuter.YMost(), aSrcInner.X()), aSkipRect); + + // Middle part + if (aMiddle) { + RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur, + RectWithEdgesTRBL(aDstInner.Y(), aDstInner.XMost(), + aDstInner.YMost(), aDstInner.X()), + RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.XMost(), + aSrcInner.YMost(), aSrcInner.X()), + aSkipRect); + } +} + +static void +DrawMirroredRect(DrawTarget* aDT, + SourceSurface* aSurface, + const Rect& aDest, const Point& aSrc, + Float aScaleX, Float aScaleY) +{ + SurfacePattern pattern(aSurface, ExtendMode::CLAMP, + Matrix::Scaling(aScaleX, aScaleY) + .PreTranslate(-aSrc) + .PostTranslate( + aScaleX < 0 ? aDest.XMost() : aDest.x, + aScaleY < 0 ? aDest.YMost() : aDest.y)); + aDT->FillRect(aDest, pattern); +} + +static void +DrawMirroredBoxShadow(DrawTarget* aDT, + SourceSurface* aSurface, + const Rect& aDestRect) +{ + Point center(ceil(aDestRect.x + aDestRect.width / 2), + ceil(aDestRect.y + aDestRect.height / 2)); + Rect topLeft(aDestRect.x, aDestRect.y, + center.x - aDestRect.x, + center.y - aDestRect.y); + Rect bottomRight(topLeft.BottomRight(), aDestRect.Size() - topLeft.Size()); + Rect topRight(bottomRight.x, topLeft.y, bottomRight.width, topLeft.height); + Rect bottomLeft(topLeft.x, bottomRight.y, topLeft.width, bottomRight.height); + DrawMirroredRect(aDT, aSurface, topLeft, Point(), 1, 1); + DrawMirroredRect(aDT, aSurface, topRight, Point(), -1, 1); + DrawMirroredRect(aDT, aSurface, bottomLeft, Point(), 1, -1); + DrawMirroredRect(aDT, aSurface, bottomRight, Point(), -1, -1); +} + +static void +DrawMirroredCorner(DrawTarget* aDT, SourceSurface* aSurface, + const Rect& aDest, const Point& aSrc, + const Rect& aSkipRect, Float aScaleX, Float aScaleY) +{ + if (aSkipRect.Contains(aDest)) { + return; + } + + DrawMirroredRect(aDT, aSurface, aDest, aSrc, aScaleX, aScaleY); +} + +static void +RepeatOrStretchMirroredSurface(DrawTarget* aDT, SourceSurface* aSurface, + const Rect& aDest, const Rect& aSrc, + const Rect& aSkipRect, Float aScaleX, Float aScaleY) +{ + if (aSkipRect.Contains(aDest)) { + return; + } + + if (ShouldStretchSurface(aDT, aSurface)) { + aScaleX *= aDest.width / aSrc.width; + aScaleY *= aDest.height / aSrc.height; + DrawMirroredRect(aDT, aSurface, aDest, aSrc.TopLeft(), aScaleX, aScaleY); + return; + } + + SurfacePattern pattern(aSurface, ExtendMode::REPEAT, + Matrix::Scaling(aScaleX, aScaleY) + .PreTranslate(-aSrc.TopLeft()) + .PostTranslate( + aScaleX < 0 ? aDest.XMost() : aDest.x, + aScaleY < 0 ? aDest.YMost() : aDest.y), + SamplingFilter::GOOD, RoundedToInt(aSrc)); + aDT->FillRect(aDest, pattern); +} + +static void +DrawMirroredMinBoxShadow(DrawTarget* aDestDrawTarget, SourceSurface* aSourceBlur, + const Rect& aDstOuter, const Rect& aDstInner, + const Rect& aSrcOuter, const Rect& aSrcInner, + const Rect& aSkipRect, bool aMiddle = false) +{ + // Corners: top left, top right, bottom left, bottom right + // Compute quadrant bounds and then clip them to corners along + // dimensions where we need to stretch from min size. + Point center(ceil(aDstOuter.x + aDstOuter.width / 2), + ceil(aDstOuter.y + aDstOuter.height / 2)); + Rect topLeft(aDstOuter.x, aDstOuter.y, + center.x - aDstOuter.x, + center.y - aDstOuter.y); + Rect bottomRight(topLeft.BottomRight(), aDstOuter.Size() - topLeft.Size()); + Rect topRight(bottomRight.x, topLeft.y, bottomRight.width, topLeft.height); + Rect bottomLeft(topLeft.x, bottomRight.y, topLeft.width, bottomRight.height); + + // Check if the middle part has been minimized along each dimension. + // If so, those will be strecthed/drawn separately and need to be clipped out. + if (aSrcInner.width == 1) { + topLeft.SetRightEdge(aDstInner.x); + topRight.SetLeftEdge(aDstInner.XMost()); + bottomLeft.SetRightEdge(aDstInner.x); + bottomRight.SetLeftEdge(aDstInner.XMost()); + } + if (aSrcInner.height == 1) { + topLeft.SetBottomEdge(aDstInner.y); + topRight.SetBottomEdge(aDstInner.y); + bottomLeft.SetTopEdge(aDstInner.YMost()); + bottomRight.SetTopEdge(aDstInner.YMost()); + } + + DrawMirroredCorner(aDestDrawTarget, aSourceBlur, topLeft, + aSrcOuter.TopLeft(), aSkipRect, 1, 1); + DrawMirroredCorner(aDestDrawTarget, aSourceBlur, topRight, + aSrcOuter.TopLeft(), aSkipRect, -1, 1); + DrawMirroredCorner(aDestDrawTarget, aSourceBlur, bottomLeft, + aSrcOuter.TopLeft(), aSkipRect, 1, -1); + DrawMirroredCorner(aDestDrawTarget, aSourceBlur, bottomRight, + aSrcOuter.TopLeft(), aSkipRect, -1, -1); + + // Edges: top, bottom, left, right + // Draw middle edges where they need to be stretched. The top and left + // sections that are part of the top-left quadrant will be mirrored to + // the bottom and right sections, respectively. + if (aSrcInner.width == 1) { + Rect dstTop = RectWithEdgesTRBL(aDstOuter.Y(), aDstInner.XMost(), + aDstInner.Y(), aDstInner.X()); + Rect srcTop = RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(), + aSrcInner.Y(), aSrcInner.X()); + Rect dstBottom = RectWithEdgesTRBL(aDstInner.YMost(), aDstInner.XMost(), + aDstOuter.YMost(), aDstInner.X()); + Rect srcBottom = RectWithEdgesTRBL(aSrcOuter.Y(), aSrcInner.XMost(), + aSrcInner.Y(), aSrcInner.X()); + // If we only need to stretch along the X axis and we're drawing + // the middle section, just sample all the way to the center of the + // source on the Y axis to avoid extra draw calls. + if (aMiddle && aSrcInner.height != 1) { + dstTop.SetBottomEdge(center.y); + srcTop.height = dstTop.height; + dstBottom.SetTopEdge(dstTop.YMost()); + srcBottom.height = dstBottom.height; + } + RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur, + dstTop, srcTop, aSkipRect, 1, 1); + RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur, + dstBottom, srcBottom, aSkipRect, 1, -1); + } + + if (aSrcInner.height == 1) { + Rect dstLeft = RectWithEdgesTRBL(aDstInner.Y(), aDstInner.X(), + aDstInner.YMost(), aDstOuter.X()); + Rect srcLeft = RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(), + aSrcInner.YMost(), aSrcOuter.X()); + Rect dstRight = RectWithEdgesTRBL(aDstInner.Y(), aDstOuter.XMost(), + aDstInner.YMost(), aDstInner.XMost()); + Rect srcRight = RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.X(), + aSrcInner.YMost(), aSrcOuter.X()); + // Only stretching on Y axis, so sample source to the center of the X axis. + if (aMiddle && aSrcInner.width != 1) { + dstLeft.SetRightEdge(center.x); + srcLeft.width = dstLeft.width; + dstRight.SetLeftEdge(dstLeft.XMost()); + srcRight.width = dstRight.width; + } + RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur, + dstLeft, srcLeft, aSkipRect, 1, 1); + RepeatOrStretchMirroredSurface(aDestDrawTarget, aSourceBlur, + dstRight, srcRight, aSkipRect, -1, 1); + } + + // If we need to stretch along both dimensions, then the middle part + // must be drawn separately. + if (aMiddle && aSrcInner.width == 1 && aSrcInner.height == 1) { + RepeatOrStretchSurface(aDestDrawTarget, aSourceBlur, + RectWithEdgesTRBL(aDstInner.Y(), aDstInner.XMost(), + aDstInner.YMost(), aDstInner.X()), + RectWithEdgesTRBL(aSrcInner.Y(), aSrcInner.XMost(), + aSrcInner.YMost(), aSrcInner.X()), + aSkipRect); + } } /*** @@ -695,66 +880,69 @@ DrawBoxShadows(DrawTarget& aDestDrawTarget, SourceSurface* aSourceBlur, /* static */ void gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, - RectCornerRadii* aCornerRadii, + const RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const Color& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); + bool mirrorCorners = !aCornerRadii || aCornerRadii->AreRadiiSame(); IntRect rect = RoundedToInt(ToRect(aRect)); - IntMargin extendDestBy; + IntMargin blurMargin; IntMargin slice; + IntSize minSize; RefPtr<SourceSurface> boxShadow = GetBlur(aDestinationCtx, rect.Size(), blurRadius, - aCornerRadii, aShadowColor, - extendDestBy, slice); + aCornerRadii, aShadowColor, mirrorCorners, + blurMargin, slice, minSize); if (!boxShadow) { return; } - DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget(); - destDrawTarget.PushClipRect(ToRect(aDirtyRect)); + DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); + destDrawTarget->PushClipRect(ToRect(aDirtyRect)); // Copy the right parts from boxShadow into destDrawTarget. The middle parts // will be stretched, border-image style. - Rect srcOuter(Point(), Size(boxShadow->GetSize())); - Rect srcInner = srcOuter; + Rect srcOuter(Point(blurMargin.left, blurMargin.top), Size(minSize)); + Rect srcInner(srcOuter); + srcOuter.Inflate(Margin(blurMargin)); srcInner.Deflate(Margin(slice)); - rect.Inflate(extendDestBy); Rect dstOuter(rect); Rect dstInner(rect); + dstOuter.Inflate(Margin(blurMargin)); dstInner.Deflate(Margin(slice)); Rect skipRect = ToRect(aSkipRect); - if (srcInner.IsEqualInterior(srcOuter)) { - MOZ_ASSERT(dstInner.IsEqualInterior(dstOuter)); + if (minSize == rect.Size()) { // The target rect is smaller than the minimal size so just draw the surface - destDrawTarget.DrawSurface(boxShadow, dstInner, srcInner); + if (mirrorCorners) { + DrawMirroredBoxShadow(destDrawTarget, boxShadow, dstOuter); + } else { + destDrawTarget->DrawSurface(boxShadow, dstOuter, srcOuter); + } } else { - DrawBoxShadows(destDrawTarget, boxShadow, dstOuter, dstInner, - srcOuter, srcInner, skipRect); - - // Middle part - RepeatOrStretchSurface(destDrawTarget, boxShadow, - RectWithEdgesTRBL(dstInner.Y(), dstInner.XMost(), - dstInner.YMost(), dstInner.X()), - RectWithEdgesTRBL(srcInner.Y(), srcInner.XMost(), - srcInner.YMost(), srcInner.X()), - skipRect); + if (mirrorCorners) { + DrawMirroredMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, + srcOuter, srcInner, skipRect, true); + } else { + DrawMinBoxShadow(destDrawTarget, boxShadow, dstOuter, dstInner, + srcOuter, srcInner, skipRect, true); + } } - // A note about anti-aliasing and seems between adjacent parts: + // A note about anti-aliasing and seams between adjacent parts: // We don't explicitly disable anti-aliasing in the DrawSurface calls above, // so if there's a transform on destDrawTarget that is not pixel-aligned, // there will be seams between adjacent parts of the box-shadow. It's hard to // avoid those without the use of an intermediate surface. - // You might think that we could avoid those by just turning of AA, but there + // You might think that we could avoid those by just turning off AA, but there // is a problem with that: Box-shadow rendering needs to clip out the // element's border box, and we'd like that clip to have anti-aliasing - // especially if the element has rounded corners! So we can't do that unless @@ -766,13 +954,13 @@ gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, // covered by the shape. So for pixels on the edge between two adjacent parts, // all those pixels will be painted to by both parts, which looks very bad. - destDrawTarget.PopClip(); + destDrawTarget->PopClip(); } static already_AddRefed<Path> GetBoxShadowInsetPath(DrawTarget* aDrawTarget, const Rect aOuterRect, const Rect aInnerRect, - const bool aHasBorderRadius, const RectCornerRadii& aInnerClipRadii) + const RectCornerRadii* aInnerClipRadii) { /*** * We create an inset path by having two rects. @@ -791,8 +979,8 @@ GetBoxShadowInsetPath(DrawTarget* aDrawTarget, aDrawTarget->CreatePathBuilder(FillRule::FILL_EVEN_ODD); AppendRectToPath(builder, aOuterRect, true); - if (aHasBorderRadius) { - AppendRoundedRectToPath(builder, aInnerRect, aInnerClipRadii, false); + if (aInnerClipRadii) { + AppendRoundedRectToPath(builder, aInnerRect, *aInnerClipRadii, false); } else { AppendRectToPath(builder, aInnerRect, false); } @@ -801,31 +989,28 @@ GetBoxShadowInsetPath(DrawTarget* aDrawTarget, static void FillDestinationPath(gfxContext* aDestinationCtx, - const Rect aDestinationRect, - const Rect aShadowClipRect, + const Rect& aDestinationRect, + const Rect& aShadowClipRect, const Color& aShadowColor, - const bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii) + const RectCornerRadii* aInnerClipRadii = nullptr) { // When there is no blur radius, fill the path onto the destination // surface. aDestinationCtx->SetColor(aShadowColor); DrawTarget* destDrawTarget = aDestinationCtx->GetDrawTarget(); RefPtr<Path> shadowPath = GetBoxShadowInsetPath(destDrawTarget, aDestinationRect, - aShadowClipRect, aHasBorderRadius, - aInnerClipRadii); + aShadowClipRect, aInnerClipRadii); aDestinationCtx->SetPath(shadowPath); aDestinationCtx->Fill(); } static void -CacheInsetBlur(const IntSize aMinOuterSize, - const IntSize aMinInnerSize, +CacheInsetBlur(const IntSize& aMinOuterSize, + const IntSize& aMinInnerSize, const IntSize& aBlurRadius, const RectCornerRadii* aCornerRadii, const Color& aShadowColor, - const bool& aHasBorderRadius, BackendType aBackendType, SourceSurface* aBoxShadow) { @@ -833,76 +1018,76 @@ CacheInsetBlur(const IntSize aMinOuterSize, BlurCacheKey key(aMinOuterSize, aMinInnerSize, aBlurRadius, aCornerRadii, aShadowColor, isInsetBlur, - aHasBorderRadius, aBackendType); - IntMargin extendDestBy(0, 0, 0, 0); - BlurCacheData* data = new BlurCacheData(aBoxShadow, extendDestBy, key); + aBackendType); + IntMargin blurMargin(0, 0, 0, 0); + BlurCacheData* data = new BlurCacheData(aBoxShadow, blurMargin, key); if (!gBlurCache->RegisterEntry(data)) { delete data; } } -already_AddRefed<mozilla::gfx::SourceSurface> -gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect, - const mozilla::gfx::Rect aWhitespaceRect, - const bool aIsDestRect, - const mozilla::gfx::Color& aShadowColor, - const mozilla::gfx::IntSize& aBlurRadius, - const bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii, - DrawTarget* aDestDrawTarget) +already_AddRefed<SourceSurface> +gfxAlphaBoxBlur::GetInsetBlur(const Rect& aOuterRect, + const Rect& aWhitespaceRect, + bool aIsDestRect, + const Color& aShadowColor, + const IntSize& aBlurRadius, + const RectCornerRadii* aInnerClipRadii, + DrawTarget* aDestDrawTarget, + bool aMirrorCorners) { if (!gBlurCache) { gBlurCache = new BlurCache(); } - IntSize outerSize((int)aOuterRect.width, (int)aOuterRect.height); - IntSize whitespaceSize((int)aWhitespaceRect.width, (int)aWhitespaceRect.height); - BlurCacheData* cached = + IntSize outerSize = IntSize::Truncate(aOuterRect.Size()); + IntSize whitespaceSize = IntSize::Truncate(aWhitespaceRect.Size()); + if (!aIsDestRect) { + BlurCacheData* cached = gBlurCache->LookupInsetBoxShadow(outerSize, whitespaceSize, - aBlurRadius, &aInnerClipRadii, - aShadowColor, aHasBorderRadius, - aDestDrawTarget->GetBackendType()); - - if (cached && !aIsDestRect) { - // So we don't forget the actual cached blur - RefPtr<SourceSurface> cachedBlur = cached->mBlur; - return cachedBlur.forget(); + aBlurRadius, aInnerClipRadii, + aShadowColor, aDestDrawTarget->GetBackendType()); + if (cached) { + // So we don't forget the actual cached blur + RefPtr<SourceSurface> cachedBlur = cached->mBlur; + return cachedBlur.forget(); + } } // If we can do a min rect, the whitespace rect will be expanded in Init to // aOuterRect. Rect blurRect = aIsDestRect ? aOuterRect : aWhitespaceRect; + // If mirroring corners, we only need to draw the top-left quadrant. + // Use ceil to preserve the remaining 1x1 middle area for minimized box + // shadows. + if (aMirrorCorners) { + blurRect.SizeTo(ceil(blurRect.width * 0.5f), ceil(blurRect.height * 0.5f)); + } IntSize zeroSpread(0, 0); - gfxContext* minGfxContext = Init(ThebesRect(blurRect), - zeroSpread, aBlurRadius, - nullptr, nullptr); - if (!minGfxContext) { + RefPtr<DrawTarget> minDrawTarget = + InitDrawTarget(blurRect, zeroSpread, aBlurRadius); + if (!minDrawTarget) { return nullptr; } - // This is really annoying. When we create the AlphaBoxBlur, the gfxContext + // This is really annoying. When we create the AlphaBoxBlur, the DrawTarget // has a translation applied to it that is the topLeft point. This is actually // the rect we gave it plus the blur radius. The rects we give this for the outer // and whitespace rects are based at (0, 0). We could either translate those rects // when we don't have a destination rect or ignore the translation when using // the dest rect. The dest rects layout gives us expect this translation. if (!aIsDestRect) { - minGfxContext->SetMatrix(gfxMatrix()); + minDrawTarget->SetTransform(Matrix()); } - DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget(); - // Fill in the path between the inside white space / outer rects // NOT the inner frame RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, aOuterRect, - aWhitespaceRect, aHasBorderRadius, - aInnerClipRadii); + aWhitespaceRect, aInnerClipRadii); - Color black(0.f, 0.f, 0.f, 1.f); - minGfxContext->SetColor(black); - minGfxContext->SetPath(maskPath); - minGfxContext->Fill(); + ColorPattern black(Color(0.f, 0.f, 0.f, 1.f)); + minDrawTarget->Fill(maskPath, black); // Create the A8 mask IntPoint topLeft; @@ -912,16 +1097,15 @@ gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect, } // Fill in with the color we actually wanted - RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(*aDestDrawTarget, minMask, aShadowColor); + RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(aDestDrawTarget, minMask, aShadowColor); if (!minInsetBlur) { return nullptr; } if (!aIsDestRect) { CacheInsetBlur(outerSize, whitespaceSize, - aBlurRadius, &aInnerClipRadii, - aShadowColor, aHasBorderRadius, - aDestDrawTarget->GetBackendType(), + aBlurRadius, aInnerClipRadii, + aShadowColor, aDestDrawTarget->GetBackendType(), minInsetBlur); } @@ -951,36 +1135,35 @@ gfxAlphaBoxBlur::GetInsetBlur(const mozilla::gfx::Rect aOuterRect, * | | | * |________________________________| */ -static void GetBlurMargins(const bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii, - const IntSize aBlurRadius, +static void GetBlurMargins(const RectCornerRadii* aInnerClipRadii, + const IntSize& aBlurRadius, Margin& aOutBlurMargin, Margin& aOutInnerMargin) { - float cornerWidth = 0; - float cornerHeight = 0; - if (aHasBorderRadius) { - for (size_t i = 0; i < 4; i++) { - cornerWidth = std::max(cornerWidth, aInnerClipRadii[i].width); - cornerHeight = std::max(cornerHeight, aInnerClipRadii[i].height); + Size cornerSize(0, 0); + if (aInnerClipRadii) { + const RectCornerRadii& corners = *aInnerClipRadii; + for (size_t i = 0; i < RectCorner::Count; i++) { + cornerSize.width = std::max(cornerSize.width, corners[i].width); + cornerSize.height = std::max(cornerSize.height, corners[i].height); } } // Only the inside whitespace size cares about the border radius size. // Outer sizes only care about blur. - int width = cornerWidth + aBlurRadius.width; - int height = cornerHeight + aBlurRadius.height; + IntSize margin = IntSize::Ceil(cornerSize) + aBlurRadius; - aOutInnerMargin.SizeTo(height, width, height, width); + aOutInnerMargin.SizeTo(margin.height, margin.width, + margin.height, margin.width); aOutBlurMargin.SizeTo(aBlurRadius.height, aBlurRadius.width, aBlurRadius.height, aBlurRadius.width); } static bool -GetInsetBoxShadowRects(const Margin aBlurMargin, - const Margin aInnerMargin, - const Rect aShadowClipRect, - const Rect aDestinationRect, +GetInsetBoxShadowRects(const Margin& aBlurMargin, + const Margin& aInnerMargin, + const Rect& aShadowClipRect, + const Rect& aDestinationRect, Rect& aOutWhitespaceRect, Rect& aOutOuterRect) { @@ -1015,20 +1198,18 @@ GetInsetBoxShadowRects(const Margin aBlurMargin, void gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, - const Rect aDestinationRect, - const Rect aShadowClipRect, - const IntSize aBlurRadius, - const IntSize aSpreadRadius, + const Rect& aDestinationRect, + const Rect& aShadowClipRect, + const IntSize& aBlurRadius, const Color& aShadowColor, - bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii, - const Rect aSkipRect, - const Point aShadowOffset) + const RectCornerRadii* aInnerClipRadii, + const Rect& aSkipRect, + const Point& aShadowOffset) { if ((aBlurRadius.width == 0 && aBlurRadius.height == 0) || aShadowClipRect.IsEmpty()) { FillDestinationPath(aDestinationCtx, aDestinationRect, aShadowClipRect, - aShadowColor, aHasBorderRadius, aInnerClipRadii); + aShadowColor, aInnerClipRadii); return; } @@ -1036,31 +1217,32 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, Margin innerMargin; Margin blurMargin; - GetBlurMargins(aHasBorderRadius, aInnerClipRadii, aBlurRadius, - blurMargin, innerMargin); + GetBlurMargins(aInnerClipRadii, aBlurRadius, blurMargin, innerMargin); Rect whitespaceRect; Rect outerRect; - bool useDestRect = GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect, - aDestinationRect, whitespaceRect, outerRect); - - RefPtr<SourceSurface> minBlur = GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor, - aBlurRadius, aHasBorderRadius, aInnerClipRadii, - destDrawTarget); + bool useDestRect = + GetInsetBoxShadowRects(blurMargin, innerMargin, aShadowClipRect, + aDestinationRect, whitespaceRect, outerRect); + + bool mirrorCorners = !aInnerClipRadii || aInnerClipRadii->AreRadiiSame(); + RefPtr<SourceSurface> minBlur = + GetInsetBlur(outerRect, whitespaceRect, useDestRect, aShadowColor, + aBlurRadius, aInnerClipRadii, destDrawTarget, mirrorCorners); if (!minBlur) { return; } if (useDestRect) { - IntSize blurSize = minBlur->GetSize(); - Rect srcBlur(0, 0, blurSize.width, blurSize.height); Rect destBlur = aDestinationRect; - - // The blur itself expands the rect by the blur margin, so we - // have to mimic that here. destBlur.Inflate(blurMargin); - MOZ_ASSERT(srcBlur.Size() == destBlur.Size()); - destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur); + if (mirrorCorners) { + DrawMirroredBoxShadow(destDrawTarget, minBlur.get(), destBlur); + } else { + Rect srcBlur(Point(0, 0), Size(minBlur->GetSize())); + MOZ_ASSERT(srcBlur.Size() == destBlur.Size()); + destDrawTarget->DrawSurface(minBlur, destBlur, srcBlur); + } } else { Rect srcOuter(outerRect); Rect srcInner(srcOuter); @@ -1070,7 +1252,7 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, // The shadow clip rect already takes into account the spread radius Rect outerFillRect(aShadowClipRect); outerFillRect.Inflate(blurMargin); - FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor, false, RectCornerRadii()); + FillDestinationPath(aDestinationCtx, aDestinationRect, outerFillRect, aShadowColor); // Inflate once for the frame around the whitespace Rect destRect(aShadowClipRect); @@ -1080,9 +1262,16 @@ gfxAlphaBoxBlur::BlurInsetBox(gfxContext* aDestinationCtx, Rect destInnerRect(aShadowClipRect); destInnerRect.Deflate(innerMargin); - DrawBoxShadows(*destDrawTarget, minBlur, - destRect, destInnerRect, - srcOuter, srcInner, - aSkipRect); + if (mirrorCorners) { + DrawMirroredMinBoxShadow(destDrawTarget, minBlur, + destRect, destInnerRect, + srcOuter, srcInner, + aSkipRect); + } else { + DrawMinBoxShadow(destDrawTarget, minBlur, + destRect, destInnerRect, + srcOuter, srcInner, + aSkipRect); + } } } diff --git a/gfx/thebes/gfxBlur.h b/gfx/thebes/gfxBlur.h index 9cc5f37166..ff0a051364 100644 --- a/gfx/thebes/gfxBlur.h +++ b/gfx/thebes/gfxBlur.h @@ -10,14 +10,13 @@ #include "nsSize.h" #include "gfxPoint.h" #include "mozilla/RefPtr.h" -#include "mozilla/UniquePtr.h" +#include "mozilla/gfx/Blur.h" class gfxContext; struct gfxRect; namespace mozilla { namespace gfx { - class AlphaBoxBlur; struct Color; struct RectCornerRadii; class SourceSurface; @@ -73,21 +72,19 @@ public: * represents an area where blurring is unnecessary and shouldn't be done * for speed reasons. It is safe to pass nullptr here. */ - gfxContext* Init(const gfxRect& aRect, - const mozilla::gfx::IntSize& aSpreadRadius, - const mozilla::gfx::IntSize& aBlurRadius, - const gfxRect* aDirtyRect, - const gfxRect* aSkipRect); - - /** - * Returns the context that should be drawn to supply the alpha mask to be - * blurred. If the returned surface is null, then there was an error in - * its creation. - */ - gfxContext* GetContext() - { - return mContext; - } + already_AddRefed<gfxContext> + Init(const gfxRect& aRect, + const mozilla::gfx::IntSize& aSpreadRadius, + const mozilla::gfx::IntSize& aBlurRadius, + const gfxRect* aDirtyRect, + const gfxRect* aSkipRect); + + already_AddRefed<DrawTarget> + InitDrawTarget(const mozilla::gfx::Rect& aRect, + const mozilla::gfx::IntSize& aSpreadRadius, + const mozilla::gfx::IntSize& aBlurRadius, + const mozilla::gfx::Rect* aDirtyRect = nullptr, + const mozilla::gfx::Rect* aSkipRect = nullptr); already_AddRefed<mozilla::gfx::SourceSurface> DoBlur(DrawTarget* aDT, mozilla::gfx::IntPoint* aTopLeft); @@ -129,7 +126,7 @@ public: */ static void BlurRectangle(gfxContext *aDestinationCtx, const gfxRect& aRect, - RectCornerRadii* aCornerRadii, + const RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const Color& aShadowColor, const gfxRect& aDirtyRect, @@ -154,41 +151,34 @@ public: * @param aSKipRect An area in device pixels we don't have to paint in. */ void BlurInsetBox(gfxContext* aDestinationCtx, - const mozilla::gfx::Rect aDestinationRect, - const mozilla::gfx::Rect aShadowClipRect, - const mozilla::gfx::IntSize aBlurRadius, - const mozilla::gfx::IntSize aSpreadRadius, + const mozilla::gfx::Rect& aDestinationRect, + const mozilla::gfx::Rect& aShadowClipRect, + const mozilla::gfx::IntSize& aBlurRadius, const mozilla::gfx::Color& aShadowColor, - const bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii, - const mozilla::gfx::Rect aSkipRect, - const mozilla::gfx::Point aShadowOffset); + const RectCornerRadii* aInnerClipRadii, + const mozilla::gfx::Rect& aSkipRect, + const mozilla::gfx::Point& aShadowOffset); protected: already_AddRefed<mozilla::gfx::SourceSurface> - GetInsetBlur(const mozilla::gfx::Rect aOuterRect, - const mozilla::gfx::Rect aWhitespaceRect, - const bool aIsDestRect, + GetInsetBlur(const mozilla::gfx::Rect& aOuterRect, + const mozilla::gfx::Rect& aWhitespaceRect, + bool aIsDestRect, const mozilla::gfx::Color& aShadowColor, const mozilla::gfx::IntSize& aBlurRadius, - const bool aHasBorderRadius, - const RectCornerRadii& aInnerClipRadii, - DrawTarget* aDestDrawTarget); - - /** - * The context of the temporary alpha surface. - */ - RefPtr<gfxContext> mContext; + const RectCornerRadii* aInnerClipRadii, + DrawTarget* aDestDrawTarget, + bool aMirrorCorners); /** * The temporary alpha surface. */ - mozilla::UniquePtr<unsigned char[]> mData; + uint8_t* mData; /** * The object that actually does the blurring for us. */ - mozilla::UniquePtr<mozilla::gfx::AlphaBoxBlur> mBlur; + mozilla::gfx::AlphaBoxBlur mBlur; }; #endif /* GFX_BLUR_H */ diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 5e1ce69abc..d430230894 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1625,7 +1625,6 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext, // transparent in the shadow, so drawing them changes nothing. gfxContext* renderContext = aRenderingContext.ThebesContext(); DrawTarget* drawTarget = renderContext->GetDrawTarget(); - nsContextBoxBlur blurringArea; // Clip the context to the area of the frame's padding rect, so no part of the // shadow is painted outside. Also cut out anything beyond where the inset shadow @@ -6237,9 +6236,8 @@ nsContextBoxBlur::InsetBoxBlur(gfxContext* aDestinationCtx, mAlphaBoxBlur.BlurInsetBox(aDestinationCtx, transformedDestRect, transformedShadowClipRect, - blurRadius, spreadRadius, - aShadowColor, aHasBorderRadius, - aInnerClipRectRadii, transformedSkipRect, - aShadowOffset); + blurRadius, aShadowColor, + aHasBorderRadius ? &aInnerClipRectRadii : nullptr, + transformedSkipRect, aShadowOffset); return true; } |