author | Mason Chang <mchang@mozilla.com> |
Thu, 25 Jun 2015 12:04:21 -0700 | |
changeset 250212 | 869aca2db34d2a3f5a7afe2a6449b2c91d62e604 |
parent 250211 | 28bbd1fb7ed10f2ec33b1b0129ed3ad335e5ae59 |
child 250213 | 5ddf0a252b086c7e8e468243c6251af5c0b1da57 |
push id | 61480 |
push user | mchang@mozilla.com |
push date | Thu, 25 Jun 2015 19:04:33 +0000 |
treeherder | mozilla-inbound@869aca2db34d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mstange |
bugs | 1162824 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
gfx/2d/PathHelpers.h | file | annotate | diff | comparison | revisions | |
gfx/thebes/gfxBlur.cpp | file | annotate | diff | comparison | revisions |
--- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -187,16 +187,23 @@ struct RectCornerRadii { const Size& operator[](size_t aCorner) const { return radii[aCorner]; } Size& operator[](size_t aCorner) { return radii[aCorner]; } + bool operator==(const RectCornerRadii& aOther) const { + for (size_t i = 0; i < RectCorner::Count; i++) { + if (radii[i] != aOther.radii[i]) return false; + } + return true; + } + void Scale(Float aXScale, Float aYScale) { for (int i = 0; i < RectCorner::Count; i++) { radii[i].Scale(aXScale, aYScale); } } const Size TopLeft() const { return radii[RectCorner::TopLeft]; } Size& TopLeft() { return radii[RectCorner::TopLeft]; }
--- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -160,88 +160,102 @@ IntSize gfxAlphaBoxBlur::CalculateBlurRa return IntSize(size.width, size.height); } struct BlurCacheKey : public PLDHashEntryHdr { typedef const BlurCacheKey& KeyType; typedef const BlurCacheKey* KeyTypePointer; enum { ALLOW_MEMMOVE = true }; - gfxRect mRect; + IntSize mMinSize; IntSize mBlurRadius; - gfxRect mSkipRect; + gfxRGBA mShadowColor; BackendType mBackend; + RectCornerRadii mCornerRadii; - BlurCacheKey(const gfxRect& aRect, const IntSize &aBlurRadius, const gfxRect& aSkipRect, BackendType aBackend) - : mRect(aRect) + BlurCacheKey(IntSize aMinimumSize, gfxIntSize aBlurRadius, + RectCornerRadii* aCornerRadii, gfxRGBA aShadowColor, + BackendType aBackend) + : mMinSize(aMinimumSize) , mBlurRadius(aBlurRadius) - , mSkipRect(aSkipRect) + , mShadowColor(aShadowColor) , mBackend(aBackend) + , mCornerRadii(aCornerRadii ? *aCornerRadii : RectCornerRadii()) { } explicit BlurCacheKey(const BlurCacheKey* aOther) - : mRect(aOther->mRect) + : mMinSize(aOther->mMinSize) , mBlurRadius(aOther->mBlurRadius) - , mSkipRect(aOther->mSkipRect) + , mShadowColor(aOther->mShadowColor) , mBackend(aOther->mBackend) + , mCornerRadii(aOther->mCornerRadii) { } static PLDHashNumber HashKey(const KeyTypePointer aKey) { - PLDHashNumber hash = HashBytes(&aKey->mRect.x, 4 * sizeof(gfxFloat)); + PLDHashNumber hash = 0; + hash = AddToHash(hash, aKey->mMinSize.width, aKey->mMinSize.height); hash = AddToHash(hash, aKey->mBlurRadius.width, aKey->mBlurRadius.height); - hash = AddToHash(hash, HashBytes(&aKey->mSkipRect.x, 4 * sizeof(gfxFloat))); + + hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.r, sizeof(gfxFloat))); + hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.g, sizeof(gfxFloat))); + hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.b, sizeof(gfxFloat))); + hash = AddToHash(hash, HashBytes(&aKey->mShadowColor.a, sizeof(gfxFloat))); + + for (int i = 0; i < 4; i++) { + hash = AddToHash(hash, aKey->mCornerRadii[i].width, aKey->mCornerRadii[i].height); + } + hash = AddToHash(hash, (uint32_t)aKey->mBackend); return hash; } bool KeyEquals(KeyTypePointer aKey) const { - if (aKey->mRect.IsEqualInterior(mRect) && + if (aKey->mMinSize == mMinSize && aKey->mBlurRadius == mBlurRadius && - aKey->mSkipRect.IsEqualInterior(mSkipRect) && + aKey->mCornerRadii == mCornerRadii && + aKey->mShadowColor == mShadowColor && aKey->mBackend == mBackend) { return true; - } - return false; + } + + return false; } static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } }; /** * This class is what is cached. It need to be allocated in an object separated * to the cache entry to be able to be tracked by the nsExpirationTracker. * */ struct BlurCacheData { - BlurCacheData(SourceSurface* aBlur, const IntPoint& aTopLeft, const gfxRect& aDirtyRect, const BlurCacheKey& aKey) + BlurCacheData(SourceSurface* aBlur, IntMargin aExtendDestBy, const BlurCacheKey& aKey) : mBlur(aBlur) - , mTopLeft(aTopLeft) - , mDirtyRect(aDirtyRect) + , mExtendDest(aExtendDestBy) , mKey(aKey) {} BlurCacheData(const BlurCacheData& aOther) : mBlur(aOther.mBlur) - , mTopLeft(aOther.mTopLeft) - , mDirtyRect(aOther.mDirtyRect) + , mExtendDest(aOther.mExtendDest) , mKey(aOther.mKey) { } nsExpirationState *GetExpirationState() { return &mExpirationState; } nsExpirationState mExpirationState; RefPtr<SourceSurface> mBlur; - IntPoint mTopLeft; - gfxRect mDirtyRect; + IntMargin mExtendDest; BlurCacheKey mKey; }; /** * This class implements a cache with no maximum size, that retains the * SourceSurfaces used to draw the blurs. * * An entry stays in the cache as long as it is used often. @@ -255,29 +269,27 @@ class BlurCache final : public nsExpirat } virtual void NotifyExpired(BlurCacheData* aObject) { RemoveObject(aObject); mHashEntries.Remove(aObject->mKey); } - BlurCacheData* Lookup(const gfxRect& aRect, - const IntSize& aBlurRadius, - const gfxRect& aSkipRect, - BackendType aBackendType, - const gfxRect* aDirtyRect) + BlurCacheData* Lookup(const IntSize aMinSize, + const gfxIntSize& aBlurRadius, + RectCornerRadii* aCornerRadii, + const gfxRGBA& aShadowColor, + BackendType aBackendType) { BlurCacheData* blur = - mHashEntries.Get(BlurCacheKey(aRect, aBlurRadius, aSkipRect, aBackendType)); - + mHashEntries.Get(BlurCacheKey(aMinSize, aBlurRadius, + aCornerRadii, aShadowColor, + aBackendType)); if (blur) { - if (aDirtyRect && !blur->mDirtyRect.Contains(*aDirtyRect)) { - return nullptr; - } MarkUsed(blur); } return blur; } // Returns true if we successfully register the blur in the cache, false // otherwise. @@ -302,158 +314,120 @@ class BlurCache final : public nsExpirat * FIXME use nsTHashtable to avoid duplicating the BlurCacheKey. * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47 */ nsClassHashtable<BlurCacheKey, BlurCacheData> mHashEntries; }; static BlurCache* gBlurCache = nullptr; -SourceSurface* -GetCachedBlur(DrawTarget *aDT, - const gfxRect& aRect, - const IntSize& aBlurRadius, - const gfxRect& aSkipRect, - const gfxRect& aDirtyRect, - IntPoint* aTopLeft) -{ - if (!gBlurCache) { - gBlurCache = new BlurCache(); - } - BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect, - aDT->GetBackendType(), - &aDirtyRect); - if (cached) { - *aTopLeft = cached->mTopLeft; - return cached->mBlur; - } - return nullptr; -} - -void -CacheBlur(DrawTarget *aDT, - const gfxRect& aRect, - const IntSize& aBlurRadius, - const gfxRect& aSkipRect, - SourceSurface* aBlur, - const IntPoint& aTopLeft, - const gfxRect& aDirtyRect) -{ - // If we already had a cached value with this key, but an incorrect dirty region then just update - // the existing entry - if (BlurCacheData* cached = gBlurCache->Lookup(aRect, aBlurRadius, aSkipRect, - aDT->GetBackendType(), - nullptr)) { - cached->mBlur = aBlur; - cached->mTopLeft = aTopLeft; - cached->mDirtyRect = aDirtyRect; - return; - } - - BlurCacheKey key(aRect, aBlurRadius, aSkipRect, aDT->GetBackendType()); - BlurCacheData* data = new BlurCacheData(aBlur, aTopLeft, aDirtyRect, key); - if (!gBlurCache->RegisterEntry(data)) { - delete data; - } -} - -void -gfxAlphaBoxBlur::ShutdownBlurCache() -{ - delete gBlurCache; - gBlurCache = nullptr; -} - static IntSize -ComputeMinimalSizeForShadowShape(RectCornerRadii* aCornerRadii, - gfxIntSize aBlurRadius, - IntMargin& aSlice) +ComputeMinSizeForShadowShape(RectCornerRadii* aCornerRadii, + gfxIntSize aBlurRadius, + IntMargin& aSlice, + const IntSize& aRectSize) { float cornerWidth = 0; float cornerHeight = 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); } } aSlice = IntMargin(ceil(cornerHeight) + aBlurRadius.height, ceil(cornerWidth) + aBlurRadius.width, ceil(cornerHeight) + aBlurRadius.height, ceil(cornerWidth) + aBlurRadius.width); - // Include 1 pixel for the stretchable strip in the middle. - return IntSize(aSlice.LeftRight() + 1, - aSlice.TopBottom() + 1); + IntSize minSize(aSlice.LeftRight() + 1, + aSlice.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 + // image such that the result looks correct. So we need to adjust minSize + // in such a way that we can later draw it without stretching in the affected + // dimension. We also need to adjust "slice" to ensure that we're not trying + // to slice away more than we have. + if (aRectSize.width < minSize.width) { + minSize.width = aRectSize.width; + aSlice.left = 0; + aSlice.right = 0; + } + if (aRectSize.height < minSize.height) { + minSize.height = aRectSize.height; + aSlice.top = 0; + aSlice.bottom = 0; + } + + MOZ_ASSERT(aSlice.LeftRight() <= minSize.width); + MOZ_ASSERT(aSlice.TopBottom() <= minSize.height); + return minSize; +} + +void +CacheBlur(DrawTarget& aDT, + const IntSize& aMinSize, + const gfxIntSize& aBlurRadius, + RectCornerRadii* aCornerRadii, + const gfxRGBA& aShadowColor, + IntMargin aExtendDest, + SourceSurface* aBoxShadow) +{ + BlurCacheKey key(aMinSize, aBlurRadius, aCornerRadii, aShadowColor, aDT.GetBackendType()); + BlurCacheData* data = new BlurCacheData(aBoxShadow, aExtendDest, key); + if (!gBlurCache->RegisterEntry(data)) { + delete data; + } } // Blurs a small surface and creates the mask. static TemporaryRef<SourceSurface> CreateBlurMask(const IntSize& aRectSize, RectCornerRadii* aCornerRadii, gfxIntSize aBlurRadius, IntMargin& aExtendDestBy, IntMargin& aSliceBorder, DrawTarget& aDestDrawTarget) { IntMargin slice; - IntSize minimalSize = - ComputeMinimalSizeForShadowShape(aCornerRadii, aBlurRadius, slice); + gfxAlphaBoxBlur blur; + IntSize minSize = + ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, slice, aRectSize); + IntRect minRect(IntPoint(), minSize); - // If aRectSize is smaller than minimalSize, the border-image approach won't - // work; there's no way to squeeze parts of the minimal box-shadow source - // image such that the result looks correct. So we need to adjust minimalSize - // in such a way that we can later draw it without stretching in the affected - // dimension. We also need to adjust "slice" to ensure that we're not trying - // to slice away more than we have. - if (aRectSize.width < minimalSize.width) { - minimalSize.width = aRectSize.width; - slice.left = 0; - slice.right = 0; - } - if (aRectSize.height < minimalSize.height) { - minimalSize.height = aRectSize.height; - slice.top = 0; - slice.bottom = 0; - } + gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minRect)), gfxIntSize(), + aBlurRadius, nullptr, nullptr); - MOZ_ASSERT(slice.LeftRight() <= minimalSize.width); - MOZ_ASSERT(slice.TopBottom() <= minimalSize.height); - - IntRect minimalRect(IntPoint(), minimalSize); - - gfxAlphaBoxBlur blur; - gfxContext* blurCtx = blur.Init(ThebesRect(Rect(minimalRect)), gfxIntSize(), - aBlurRadius, nullptr, nullptr); if (!blurCtx) { 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(minimalRect), *aCornerRadii); + MakePathForRoundedRect(*blurDT, Rect(minRect), *aCornerRadii); blurDT->Fill(roundedRect, black); } else { - blurDT->FillRect(Rect(minimalRect), black); + blurDT->FillRect(Rect(minRect), black); } IntPoint topLeft; RefPtr<SourceSurface> result = blur.DoBlur(&aDestDrawTarget, &topLeft); - IntRect expandedMinimalRect(topLeft, result->GetSize()); - aExtendDestBy = expandedMinimalRect - minimalRect; + IntRect expandedMinRect(topLeft, result->GetSize()); + aExtendDestBy = expandedMinRect - minRect; aSliceBorder = slice + aExtendDestBy; - MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinimalRect.width); - MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinimalRect.height); + MOZ_ASSERT(aSliceBorder.LeftRight() <= expandedMinRect.width); + MOZ_ASSERT(aSliceBorder.TopBottom() <= expandedMinRect.height); return result.forget(); } static TemporaryRef<SourceSurface> CreateBoxShadow(DrawTarget& aDT, SourceSurface* aBlurMask, const gfxRGBA& aShadowColor) { IntSize blurredSize = aBlurMask->GetSize(); @@ -462,16 +436,61 @@ CreateBoxShadow(DrawTarget& aDT, SourceS platform->CreateOffscreenContentDrawTarget(blurredSize, SurfaceFormat::B8G8R8A8); MOZ_ASSERT(boxShadowDT->GetType() == aDT.GetType()); ColorPattern shadowColor(ToDeviceColor(aShadowColor)); boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0)); return boxShadowDT->Snapshot(); } +SourceSurface* +GetBlur(DrawTarget& aDT, + const IntSize& aRectSize, + const gfxIntSize& aBlurRadius, + RectCornerRadii* aCornerRadii, + const gfxRGBA& aShadowColor, + IntMargin& aExtendDestBy, + IntMargin& aSlice) +{ + if (!gBlurCache) { + gBlurCache = new BlurCache(); + } + + IntSize minSize = + ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, aRectSize); + + BlurCacheData* cached = gBlurCache->Lookup(minSize, aBlurRadius, + aCornerRadii, aShadowColor, + aDT.GetBackendType()); + if (cached) { + // See CreateBlurMask() for these values + aExtendDestBy = cached->mExtendDest; + aSlice = aSlice + aExtendDestBy; + return cached->mBlur; + } + + RefPtr<SourceSurface> blurMask = + CreateBlurMask(aRectSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, aDT); + + if (!blurMask) { + return nullptr; + } + + RefPtr<SourceSurface> boxShadow = CreateBoxShadow(aDT, blurMask, aShadowColor); + CacheBlur(aDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow); + return boxShadow; +} + +void +gfxAlphaBoxBlur::ShutdownBlurCache() +{ + delete gBlurCache; + gBlurCache = nullptr; +} + static Rect RectWithEdgesTRBL(Float aTop, Float aRight, Float aBottom, Float aLeft) { return Rect(aLeft, aTop, aRight - aLeft, aBottom - aTop); } static void RepeatOrStretchSurface(DrawTarget& aDT, SourceSurface* aSurface, @@ -520,39 +539,39 @@ DrawCorner(DrawTarget& aDT, SourceSurfac * with the given shadow color to create a minimally-sized box shadow of the * right color. Finally, we cut out the 9 parts from the box-shadow source and * paint each part in the right place, stretching the non-corner parts to fill * the space between the corners. */ /* static */ void -gfxAlphaBoxBlur::BlurRectangle(gfxContext *aDestinationCtx, +gfxAlphaBoxBlur::BlurRectangle(gfxContext* aDestinationCtx, const gfxRect& aRect, RectCornerRadii* aCornerRadii, const gfxPoint& aBlurStdDev, const gfxRGBA& aShadowColor, const gfxRect& aDirtyRect, const gfxRect& aSkipRect) { DrawTarget& destDrawTarget = *aDestinationCtx->GetDrawTarget(); IntSize blurRadius = CalculateBlurRadius(aBlurStdDev); IntRect rect = RoundedToInt(ToRect(aRect)); IntMargin extendDestBy; IntMargin slice; - RefPtr<SourceSurface> blurMask = - CreateBlurMask(rect.Size(), aCornerRadii, blurRadius, extendDestBy, slice, - destDrawTarget); - if (!blurMask) { + + RefPtr<SourceSurface> boxShadow = GetBlur(destDrawTarget, + rect.Size(), blurRadius, + aCornerRadii, aShadowColor, + extendDestBy, slice); + if (!boxShadow) { return; } - RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDrawTarget, blurMask, aShadowColor); - 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; srcInner.Deflate(Margin(slice));