static already_AddRefed<SourceSurface> GetBlur(gfxContext* aDestinationCtx, const IntSize& aRectSize, const IntSize& aBlurRadius, RectCornerRadii* aCornerRadii, const Color& aShadowColor, IntMargin& aExtendDestBy, IntMargin& aSlice) { if (!gBlurCache) { gBlurCache = new BlurCache(); } IntSize minSize = ComputeMinSizeForShadowShape(aCornerRadii, aBlurRadius, aSlice, 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, // fallback to just rendering the destination rect. Matrix destMatrix = ToMatrix(aDestinationCtx->CurrentMatrix()); bool useDestRect = !destMatrix.IsRectilinear() || destMatrix.HasNonIntegerTranslation(); 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(); } RefPtr<SourceSurface> blurMask = CreateBlurMask(minSize, aCornerRadii, aBlurRadius, aExtendDestBy, aSlice, destDT); if (!blurMask) { return nullptr; } RefPtr<SourceSurface> boxShadow = CreateBoxShadow(blurMask, aShadowColor); if (!boxShadow) { return nullptr; } if (useDestRect) { // Since we're just going to paint the actual rect to the destination aSlice.SizeTo(0, 0, 0, 0); } else { CacheBlur(destDT, minSize, aBlurRadius, aCornerRadii, aShadowColor, aExtendDestBy, boxShadow); } return boxShadow.forget(); }
already_AddRefed<mozilla::gfx::SourceSurface> gfxAlphaBoxBlur::GetInsetBlur(IntMargin& aExtendDestBy, IntMargin& aSlice, const Rect aDestinationRect, const Rect aShadowClipRect, const IntSize& aBlurRadius, const IntSize& aSpreadRadius, const RectCornerRadii& aInnerClipRadii, const Color& aShadowColor, const bool& aHasBorderRadius, const Point aShadowOffset, bool& aMovedOffset, DrawTarget* aDestDrawTarget) { if (!gBlurCache) { gBlurCache = new BlurCache(); } IntRect outerRect; IntRect innerRect; ComputeRectsForInsetBoxShadow(aBlurRadius, aSpreadRadius, outerRect, innerRect, aSlice, aDestinationRect, aShadowClipRect, aHasBorderRadius, aInnerClipRadii); // If we have a shadow offset larger than the min rect, // there's no clean way we can properly create a min rect with the offset // in the correct place and still render correctly. In those cases, // fallback to just rendering the dest rect as is. bool useDestRect = (std::abs(aShadowOffset.x) > aSlice.left) || (std::abs(aShadowOffset.y) > aSlice.top); aMovedOffset = false; if (useDestRect) { aDestinationRect.ToIntRect(&outerRect); aShadowClipRect.ToIntRect(&innerRect); aMovedOffset = true; } BlurCacheData* cached = gBlurCache->LookupInsetBoxShadow(outerRect.Size(), innerRect.Size(), aBlurRadius, aSpreadRadius, &aInnerClipRadii, aShadowColor, aHasBorderRadius, aDestDrawTarget->GetBackendType()); if (cached && !useDestRect) { aExtendDestBy = cached->mExtendDest; // Need to extend it twice: once for the outer rect and once for the inner rect. aSlice += aExtendDestBy; aSlice += aExtendDestBy; // So we don't forget the actual cached blur RefPtr<SourceSurface> cachedBlur = cached->mBlur; return cachedBlur.forget(); } // Dirty rect and skip rect are null for the min inset shadow. // When rendering inset box shadows, we respect the spread radius by changing // the shape of the unblurred shadow, and can pass a spread radius of zero here. IntSize zeroSpread(0, 0); gfxContext* minGfxContext = Init(ThebesRect(outerRect), zeroSpread, aBlurRadius, nullptr, nullptr); if (!minGfxContext) { return nullptr; } DrawTarget* minDrawTarget = minGfxContext->GetDrawTarget(); RefPtr<Path> maskPath = GetBoxShadowInsetPath(minDrawTarget, IntRectToRect(outerRect), IntRectToRect(innerRect), aHasBorderRadius, aInnerClipRadii); Color black(0.f, 0.f, 0.f, 1.f); minGfxContext->SetColor(black); minGfxContext->SetPath(maskPath); minGfxContext->Fill(); IntPoint topLeft; RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft); if (!minMask) { return nullptr; } RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(minMask, aShadowColor); if (!minInsetBlur) { return nullptr; } IntRect blurRect(topLeft, minInsetBlur->GetSize()); aExtendDestBy = blurRect - outerRect; if (useDestRect) { // Since we're just going to paint the actual rect to the destination aSlice.SizeTo(0, 0, 0, 0); } else { aSlice += aExtendDestBy; aSlice += aExtendDestBy; CacheInsetBlur(outerRect.Size(), innerRect.Size(), aBlurRadius, aSpreadRadius, &aInnerClipRadii, aShadowColor, aHasBorderRadius, aDestDrawTarget->GetBackendType(), aExtendDestBy, minInsetBlur); } return minInsetBlur.forget(); }