1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "gfxContext.h"
10 #include "gfxPlatform.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/gfx/Blur.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include "mozilla/Maybe.h"
15 #include "nsExpirationTracker.h"
16 #include "nsClassHashtable.h"
21 using namespace mozilla
;
22 using namespace mozilla::gfx
;
24 gfxAlphaBoxBlur::~gfxAlphaBoxBlur() = default;
26 UniquePtr
<gfxContext
> gfxAlphaBoxBlur::Init(gfxContext
* aDestinationCtx
,
28 const IntSize
& aSpreadRadius
,
29 const IntSize
& aBlurRadius
,
30 const gfxRect
* aDirtyRect
,
31 const gfxRect
* aSkipRect
,
32 bool aUseHardwareAccel
) {
33 DrawTarget
* refDT
= aDestinationCtx
->GetDrawTarget();
34 Maybe
<Rect
> dirtyRect
= aDirtyRect
? Some(ToRect(*aDirtyRect
)) : Nothing();
35 Maybe
<Rect
> skipRect
= aSkipRect
? Some(ToRect(*aSkipRect
)) : Nothing();
36 RefPtr
<DrawTarget
> dt
= InitDrawTarget(
37 refDT
, ToRect(aRect
), aSpreadRadius
, aBlurRadius
,
38 dirtyRect
.ptrOr(nullptr), skipRect
.ptrOr(nullptr), aUseHardwareAccel
);
39 if (!dt
|| !dt
->IsValid()) {
43 auto context
= MakeUnique
<gfxContext
>(dt
);
44 context
->SetMatrix(Matrix::Translation(-mBlur
.GetRect().TopLeft()));
48 already_AddRefed
<DrawTarget
> gfxAlphaBoxBlur::InitDrawTarget(
49 const DrawTarget
* aReferenceDT
, const Rect
& aRect
,
50 const IntSize
& aSpreadRadius
, const IntSize
& aBlurRadius
,
51 const Rect
* aDirtyRect
, const Rect
* aSkipRect
, bool aUseHardwareAccel
) {
52 mBlur
.Init(aRect
, aSpreadRadius
, aBlurRadius
, aDirtyRect
, aSkipRect
);
53 size_t blurDataSize
= mBlur
.GetSurfaceAllocationSize();
54 if (blurDataSize
== 0) {
58 BackendType backend
= aReferenceDT
->GetBackendType();
60 // Check if the backend has an accelerated DrawSurfaceWithShadow.
61 // Currently, only D2D1.1 supports this.
62 // Otherwise, DrawSurfaceWithShadow only supports square blurs without spread.
63 // When blurring small draw targets such as short spans text, the cost of
64 // creating and flushing an accelerated draw target may exceed the speedup
65 // gained from the faster blur. It's up to the users of this blur
66 // to determine whether they want to use hardware acceleration.
67 if (aBlurRadius
.IsSquare() && aSpreadRadius
.IsEmpty() && aUseHardwareAccel
&&
68 backend
== BackendType::DIRECT2D1_1
) {
73 // Note: CreateShadowDrawTarget is only implemented for Cairo.
74 mDrawTarget
= aReferenceDT
->CreateShadowDrawTarget(
75 mBlur
.GetSize(), SurfaceFormat::A8
,
76 AlphaBoxBlur::CalculateBlurSigma(aBlurRadius
.width
));
78 // See Bug 1526045 - this is to force DT initialization.
79 mDrawTarget
->ClearRect(gfx::Rect());
82 // Make an alpha-only surface to draw on. We will play with the data after
83 // everything is drawn to create a blur effect.
84 // This will be freed when the DrawTarget dies
85 mData
= static_cast<uint8_t*>(calloc(1, blurDataSize
));
90 Factory::DoesBackendSupportDataDrawtarget(backend
)
91 ? Factory::CreateDrawTargetForData(backend
, mData
, mBlur
.GetSize(),
94 : gfxPlatform::CreateDrawTargetForData(
95 mData
, mBlur
.GetSize(), mBlur
.GetStride(), SurfaceFormat::A8
);
98 if (!mDrawTarget
|| !mDrawTarget
->IsValid()) {
107 mDrawTarget
->AddUserData(reinterpret_cast<UserDataKey
*>(mDrawTarget
.get()),
111 mDrawTarget
->SetTransform(Matrix::Translation(-mBlur
.GetRect().TopLeft()));
112 return do_AddRef(mDrawTarget
);
115 already_AddRefed
<SourceSurface
> gfxAlphaBoxBlur::DoBlur(
116 const sRGBColor
* aShadowColor
, IntPoint
* aOutTopLeft
) {
118 *aOutTopLeft
= mBlur
.GetRect().TopLeft();
121 RefPtr
<SourceSurface
> blurMask
;
124 blurMask
= mDrawTarget
->Snapshot();
125 } else if (mAccelerated
) {
126 blurMask
= mDrawTarget
->Snapshot();
127 RefPtr
<DrawTarget
> blurDT
= mDrawTarget
->CreateSimilarDrawTarget(
128 blurMask
->GetSize(), SurfaceFormat::A8
);
132 blurDT
->DrawSurfaceWithShadow(
133 blurMask
, Point(0, 0),
135 DeviceColor::MaskOpaqueWhite(), Point(0, 0),
136 AlphaBoxBlur::CalculateBlurSigma(mBlur
.GetBlurRadius().width
)),
137 CompositionOp::OP_OVER
);
138 blurMask
= blurDT
->Snapshot();
142 return blurMask
.forget();
145 RefPtr
<DrawTarget
> shadowDT
= mDrawTarget
->CreateSimilarDrawTarget(
146 blurMask
->GetSize(), SurfaceFormat::B8G8R8A8
);
150 ColorPattern
shadowColor(ToDeviceColor(*aShadowColor
));
151 shadowDT
->MaskSurface(shadowColor
, blurMask
, Point(0, 0));
153 return shadowDT
->Snapshot();
156 void gfxAlphaBoxBlur::Paint(gfxContext
* aDestinationCtx
) {
157 if (mDrawTarget
&& !mAccelerated
&& !mData
) {
161 DrawTarget
* dest
= aDestinationCtx
->GetDrawTarget();
163 NS_WARNING("Blurring not supported for Thebes contexts!");
167 RefPtr
<gfxPattern
> thebesPat
= aDestinationCtx
->GetPattern();
168 Pattern
* pat
= thebesPat
->GetPattern(dest
, nullptr);
170 NS_WARNING("Failed to get pattern for blur!");
175 RefPtr
<SourceSurface
> mask
= DoBlur(nullptr, &topLeft
);
177 NS_ERROR("Failed to create mask!");
181 // Avoid a semi-expensive clip operation if we can, otherwise
182 // clip to the dirty rect
183 Rect
* dirtyRect
= mBlur
.GetDirtyRect();
185 dest
->PushClipRect(*dirtyRect
);
188 Matrix oldTransform
= dest
->GetTransform();
189 Matrix newTransform
= oldTransform
;
190 newTransform
.PreTranslate(topLeft
);
191 dest
->SetTransform(newTransform
);
193 dest
->MaskSurface(*pat
, mask
, Point(0, 0));
195 dest
->SetTransform(oldTransform
);
202 IntSize
gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint
& aStd
) {
203 mozilla::gfx::Point
std(Float(aStd
.x
), Float(aStd
.y
));
204 IntSize size
= AlphaBoxBlur::CalculateBlurRadius(std
);
205 return IntSize(size
.width
, size
.height
);
208 struct BlurCacheKey
: public PLDHashEntryHdr
{
209 typedef const BlurCacheKey
& KeyType
;
210 typedef const BlurCacheKey
* KeyTypePointer
;
211 enum { ALLOW_MEMMOVE
= true };
215 sRGBColor mShadowColor
;
216 BackendType mBackend
;
217 RectCornerRadii mCornerRadii
;
220 // Only used for inset blurs
221 IntSize mInnerMinSize
;
223 BlurCacheKey(const IntSize
& aMinSize
, const IntSize
& aBlurRadius
,
224 const RectCornerRadii
* aCornerRadii
,
225 const sRGBColor
& aShadowColor
, BackendType aBackendType
)
226 : BlurCacheKey(aMinSize
, IntSize(0, 0), aBlurRadius
, aCornerRadii
,
227 aShadowColor
, false, aBackendType
) {}
229 explicit BlurCacheKey(const BlurCacheKey
* aOther
)
230 : mMinSize(aOther
->mMinSize
),
231 mBlurRadius(aOther
->mBlurRadius
),
232 mShadowColor(aOther
->mShadowColor
),
233 mBackend(aOther
->mBackend
),
234 mCornerRadii(aOther
->mCornerRadii
),
235 mIsInset(aOther
->mIsInset
),
236 mInnerMinSize(aOther
->mInnerMinSize
) {}
238 explicit BlurCacheKey(const IntSize
& aOuterMinSize
,
239 const IntSize
& aInnerMinSize
,
240 const IntSize
& aBlurRadius
,
241 const RectCornerRadii
* aCornerRadii
,
242 const sRGBColor
& aShadowColor
, bool aIsInset
,
243 BackendType aBackendType
)
244 : mMinSize(aOuterMinSize
),
245 mBlurRadius(aBlurRadius
),
246 mShadowColor(aShadowColor
),
247 mBackend(aBackendType
),
248 mCornerRadii(aCornerRadii
? *aCornerRadii
: RectCornerRadii()),
250 mInnerMinSize(aInnerMinSize
) {}
252 BlurCacheKey(BlurCacheKey
&&) = default;
254 static PLDHashNumber
HashKey(const KeyTypePointer aKey
) {
255 PLDHashNumber hash
= 0;
256 hash
= AddToHash(hash
, aKey
->mMinSize
.width
, aKey
->mMinSize
.height
);
257 hash
= AddToHash(hash
, aKey
->mBlurRadius
.width
, aKey
->mBlurRadius
.height
);
260 hash
, HashBytes(&aKey
->mShadowColor
.r
, sizeof(aKey
->mShadowColor
.r
)));
262 hash
, HashBytes(&aKey
->mShadowColor
.g
, sizeof(aKey
->mShadowColor
.g
)));
264 hash
, HashBytes(&aKey
->mShadowColor
.b
, sizeof(aKey
->mShadowColor
.b
)));
266 hash
, HashBytes(&aKey
->mShadowColor
.a
, sizeof(aKey
->mShadowColor
.a
)));
268 for (int i
= 0; i
< 4; i
++) {
269 hash
= AddToHash(hash
, aKey
->mCornerRadii
[i
].width
,
270 aKey
->mCornerRadii
[i
].height
);
273 hash
= AddToHash(hash
, (uint32_t)aKey
->mBackend
);
275 if (aKey
->mIsInset
) {
276 hash
= AddToHash(hash
, aKey
->mInnerMinSize
.width
,
277 aKey
->mInnerMinSize
.height
);
282 bool KeyEquals(KeyTypePointer aKey
) const {
283 if (aKey
->mMinSize
== mMinSize
&& aKey
->mBlurRadius
== mBlurRadius
&&
284 aKey
->mCornerRadii
== mCornerRadii
&&
285 aKey
->mShadowColor
== mShadowColor
&& aKey
->mBackend
== mBackend
) {
287 return (mInnerMinSize
== aKey
->mInnerMinSize
);
296 static KeyTypePointer
KeyToPointer(KeyType aKey
) { return &aKey
; }
300 * This class is what is cached. It need to be allocated in an object separated
301 * to the cache entry to be able to be tracked by the nsExpirationTracker.
303 struct BlurCacheData
{
304 BlurCacheData(SourceSurface
* aBlur
, const IntMargin
& aBlurMargin
,
306 : mBlur(aBlur
), mBlurMargin(aBlurMargin
), mKey(std::move(aKey
)) {}
308 BlurCacheData(BlurCacheData
&& aOther
) = default;
310 nsExpirationState
* GetExpirationState() { return &mExpirationState
; }
312 nsExpirationState mExpirationState
;
313 RefPtr
<SourceSurface
> mBlur
;
314 IntMargin mBlurMargin
;
319 * This class implements a cache with no maximum size, that retains the
320 * SourceSurfaces used to draw the blurs.
322 * An entry stays in the cache as long as it is used often.
324 class BlurCache final
: public nsExpirationTracker
<BlurCacheData
, 4> {
327 : nsExpirationTracker
<BlurCacheData
, 4>(GENERATION_MS
, "BlurCache") {}
329 virtual void NotifyExpired(BlurCacheData
* aObject
) override
{
330 RemoveObject(aObject
);
331 mHashEntries
.Remove(aObject
->mKey
);
334 BlurCacheData
* Lookup(const IntSize
& aMinSize
, const IntSize
& aBlurRadius
,
335 const RectCornerRadii
* aCornerRadii
,
336 const sRGBColor
& aShadowColor
,
337 BackendType aBackendType
) {
338 BlurCacheData
* blur
= mHashEntries
.Get(BlurCacheKey(
339 aMinSize
, aBlurRadius
, aCornerRadii
, aShadowColor
, aBackendType
));
347 BlurCacheData
* LookupInsetBoxShadow(const IntSize
& aOuterMinSize
,
348 const IntSize
& aInnerMinSize
,
349 const IntSize
& aBlurRadius
,
350 const RectCornerRadii
* aCornerRadii
,
351 const sRGBColor
& aShadowColor
,
352 BackendType aBackendType
) {
353 bool insetBoxShadow
= true;
354 BlurCacheKey
key(aOuterMinSize
, aInnerMinSize
, aBlurRadius
, aCornerRadii
,
355 aShadowColor
, insetBoxShadow
, aBackendType
);
356 BlurCacheData
* blur
= mHashEntries
.Get(key
);
364 void RegisterEntry(UniquePtr
<BlurCacheData
> aValue
) {
365 nsresult rv
= AddObject(aValue
.get());
367 // We are OOM, and we cannot track this object. We don't want stall
368 // entries in the hash table (since the expiration tracker is responsible
369 // for removing the cache entries), so we avoid putting that entry in the
370 // table, which is a good thing considering we are short on memory
371 // anyway, we probably don't want to retain things.
374 mHashEntries
.InsertOrUpdate(aValue
->mKey
, std::move(aValue
));
378 static const uint32_t GENERATION_MS
= 1000;
380 * FIXME use nsTHashtable to avoid duplicating the BlurCacheKey.
381 * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
383 nsClassHashtable
<BlurCacheKey
, BlurCacheData
> mHashEntries
;
386 static BlurCache
* gBlurCache
= nullptr;
388 static IntSize
ComputeMinSizeForShadowShape(const RectCornerRadii
* aCornerRadii
,
389 const IntSize
& aBlurRadius
,
390 IntMargin
& aOutSlice
,
391 const IntSize
& aRectSize
) {
392 Size
cornerSize(0, 0);
394 const RectCornerRadii
& corners
= *aCornerRadii
;
395 for (const auto i
: mozilla::AllPhysicalCorners()) {
396 cornerSize
.width
= std::max(cornerSize
.width
, corners
[i
].width
);
397 cornerSize
.height
= std::max(cornerSize
.height
, corners
[i
].height
);
401 IntSize margin
= IntSize::Ceil(cornerSize
) + aBlurRadius
;
403 IntMargin(margin
.height
, margin
.width
, margin
.height
, margin
.width
);
405 IntSize
minSize(aOutSlice
.LeftRight() + 1, aOutSlice
.TopBottom() + 1);
407 // If aRectSize is smaller than minSize, the border-image approach won't
408 // work; there's no way to squeeze parts of the min box-shadow source
409 // image such that the result looks correct. So we need to adjust minSize
410 // in such a way that we can later draw it without stretching in the affected
411 // dimension. We also need to adjust "slice" to ensure that we're not trying
412 // to slice away more than we have.
413 if (aRectSize
.width
< minSize
.width
) {
414 minSize
.width
= aRectSize
.width
;
418 if (aRectSize
.height
< minSize
.height
) {
419 minSize
.height
= aRectSize
.height
;
421 aOutSlice
.bottom
= 0;
424 MOZ_ASSERT(aOutSlice
.LeftRight() <= minSize
.width
);
425 MOZ_ASSERT(aOutSlice
.TopBottom() <= minSize
.height
);
429 static void CacheBlur(DrawTarget
* aDT
, const IntSize
& aMinSize
,
430 const IntSize
& aBlurRadius
,
431 const RectCornerRadii
* aCornerRadii
,
432 const sRGBColor
& aShadowColor
,
433 const IntMargin
& aBlurMargin
, SourceSurface
* aBoxShadow
) {
434 gBlurCache
->RegisterEntry(MakeUnique
<BlurCacheData
>(
435 aBoxShadow
, aBlurMargin
,
436 BlurCacheKey(aMinSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
437 aDT
->GetBackendType())));
440 // Blurs a small surface and creates the colored box shadow.
441 static already_AddRefed
<SourceSurface
> CreateBoxShadow(
442 DrawTarget
* aDestDrawTarget
, const IntSize
& aMinSize
,
443 const RectCornerRadii
* aCornerRadii
, const IntSize
& aBlurRadius
,
444 const sRGBColor
& aShadowColor
, bool aMirrorCorners
,
445 IntMargin
& aOutBlurMargin
) {
446 gfxAlphaBoxBlur blur
;
447 Rect
minRect(Point(0, 0), Size(aMinSize
));
448 Rect
blurRect(minRect
);
449 // If mirroring corners, we only need to draw the top-left quadrant.
450 // Use ceil to preserve the remaining 1x1 middle area for minimized box
452 if (aMirrorCorners
) {
453 blurRect
.SizeTo(ceil(blurRect
.Width() * 0.5f
),
454 ceil(blurRect
.Height() * 0.5f
));
456 IntSize
zeroSpread(0, 0);
457 RefPtr
<DrawTarget
> blurDT
=
458 blur
.InitDrawTarget(aDestDrawTarget
, blurRect
, zeroSpread
, aBlurRadius
);
463 ColorPattern
black(DeviceColor::MaskOpaqueBlack());
466 RefPtr
<Path
> roundedRect
=
467 MakePathForRoundedRect(*blurDT
, minRect
, *aCornerRadii
);
468 blurDT
->Fill(roundedRect
, black
);
470 blurDT
->FillRect(minRect
, black
);
474 RefPtr
<SourceSurface
> result
= blur
.DoBlur(&aShadowColor
, &topLeft
);
479 // Since blurRect is at (0, 0), we can find the inflated margin by
480 // negating the new rect origin, which would have been negative if
481 // the rect was inflated.
482 aOutBlurMargin
= IntMargin(-topLeft
.y
, -topLeft
.x
, -topLeft
.y
, -topLeft
.x
);
484 return result
.forget();
487 static already_AddRefed
<SourceSurface
> GetBlur(
488 gfxContext
* aDestinationCtx
, const IntSize
& aRectSize
,
489 const IntSize
& aBlurRadius
, const RectCornerRadii
* aCornerRadii
,
490 const sRGBColor
& aShadowColor
, bool aMirrorCorners
,
491 IntMargin
& aOutBlurMargin
, IntMargin
& aOutSlice
, IntSize
& aOutMinSize
) {
493 gBlurCache
= new BlurCache();
496 IntSize minSize
= ComputeMinSizeForShadowShape(aCornerRadii
, aBlurRadius
,
497 aOutSlice
, aRectSize
);
499 // We can get seams using the min size rect when drawing to the destination
500 // rect if we have a non-pixel aligned destination transformation. In those
501 // cases, fallback to just rendering the destination rect. During printing, we
502 // record all the Moz 2d commands and replay them on the parent side with
503 // Cairo. Cairo printing uses StretchDIBits to stretch the surface. However,
504 // since our source image is only 1px for some parts, we make thousands of
505 // calls. Instead just render the blur ourself here as one image and send it
506 // over for printing.
507 // TODO: May need to change this with the blob renderer in WR since it also
509 Matrix destMatrix
= aDestinationCtx
->CurrentMatrix();
510 bool useDestRect
= !destMatrix
.IsRectilinear() ||
511 destMatrix
.HasNonIntegerTranslation() ||
512 aDestinationCtx
->GetDrawTarget()->IsRecording();
517 int32_t maxTextureSize
= gfxPlatform::MaxTextureSize();
518 if (minSize
.width
> maxTextureSize
|| minSize
.height
> maxTextureSize
) {
522 aOutMinSize
= minSize
;
524 DrawTarget
* destDT
= aDestinationCtx
->GetDrawTarget();
527 BlurCacheData
* cached
=
528 gBlurCache
->Lookup(minSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
529 destDT
->GetBackendType());
531 // See CreateBoxShadow() for these values
532 aOutBlurMargin
= cached
->mBlurMargin
;
533 RefPtr
<SourceSurface
> blur
= cached
->mBlur
;
534 return blur
.forget();
538 RefPtr
<SourceSurface
> boxShadow
=
539 CreateBoxShadow(destDT
, minSize
, aCornerRadii
, aBlurRadius
, aShadowColor
,
540 aMirrorCorners
, aOutBlurMargin
);
545 if (RefPtr
<SourceSurface
> opt
= destDT
->OptimizeSourceSurface(boxShadow
)) {
550 CacheBlur(destDT
, minSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
551 aOutBlurMargin
, boxShadow
);
553 return boxShadow
.forget();
556 void gfxAlphaBoxBlur::ShutdownBlurCache() {
558 gBlurCache
= nullptr;
561 static Rect
RectWithEdgesTRBL(Float aTop
, Float aRight
, Float aBottom
,
563 return Rect(aLeft
, aTop
, aRight
- aLeft
, aBottom
- aTop
);
566 static bool ShouldStretchSurface(DrawTarget
* aDT
, SourceSurface
* aSurface
) {
567 // Use stretching if possible, since it leads to less seams when the
568 // destination is transformed. However, don't do this if we're using cairo,
569 // because if cairo is using pixman it won't render anything for large
570 // stretch factors because pixman's internal fixed point precision is not
571 // high enough to handle those scale factors.
572 return aDT
->GetBackendType() != BackendType::CAIRO
;
575 static void RepeatOrStretchSurface(DrawTarget
* aDT
, SourceSurface
* aSurface
,
576 const Rect
& aDest
, const Rect
& aSrc
,
577 const Rect
& aSkipRect
) {
578 if (aSkipRect
.Contains(aDest
)) {
582 if (ShouldStretchSurface(aDT
, aSurface
)) {
583 aDT
->DrawSurface(aSurface
, aDest
, aSrc
);
587 SurfacePattern
pattern(aSurface
, ExtendMode::REPEAT
,
588 Matrix::Translation(aDest
.TopLeft() - aSrc
.TopLeft()),
589 SamplingFilter::GOOD
, RoundedToInt(aSrc
));
590 aDT
->FillRect(aDest
, pattern
);
593 static void DrawCorner(DrawTarget
* aDT
, SourceSurface
* aSurface
,
594 const Rect
& aDest
, const Rect
& aSrc
,
595 const Rect
& aSkipRect
) {
596 if (aSkipRect
.Contains(aDest
)) {
600 aDT
->DrawSurface(aSurface
, aDest
, aSrc
);
603 static void DrawMinBoxShadow(DrawTarget
* aDestDrawTarget
,
604 SourceSurface
* aSourceBlur
, const Rect
& aDstOuter
,
605 const Rect
& aDstInner
, const Rect
& aSrcOuter
,
606 const Rect
& aSrcInner
, const Rect
& aSkipRect
,
607 bool aMiddle
= false) {
608 // Corners: top left, top right, bottom left, bottom right
609 DrawCorner(aDestDrawTarget
, aSourceBlur
,
610 RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.X(), aDstInner
.Y(),
612 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.X(), aSrcInner
.Y(),
616 DrawCorner(aDestDrawTarget
, aSourceBlur
,
617 RectWithEdgesTRBL(aDstOuter
.Y(), aDstOuter
.XMost(), aDstInner
.Y(),
619 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcOuter
.XMost(), aSrcInner
.Y(),
623 DrawCorner(aDestDrawTarget
, aSourceBlur
,
624 RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.X(),
625 aDstOuter
.YMost(), aDstOuter
.X()),
626 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcInner
.X(),
627 aSrcOuter
.YMost(), aSrcOuter
.X()),
630 DrawCorner(aDestDrawTarget
, aSourceBlur
,
631 RectWithEdgesTRBL(aDstInner
.YMost(), aDstOuter
.XMost(),
632 aDstOuter
.YMost(), aDstInner
.XMost()),
633 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcOuter
.XMost(),
634 aSrcOuter
.YMost(), aSrcInner
.XMost()),
637 // Edges: top, left, right, bottom
638 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
639 RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.XMost(),
640 aDstInner
.Y(), aDstInner
.X()),
641 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
642 aSrcInner
.Y(), aSrcInner
.X()),
644 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
645 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.X(),
646 aDstInner
.YMost(), aDstOuter
.X()),
647 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
648 aSrcInner
.YMost(), aSrcOuter
.X()),
651 RepeatOrStretchSurface(
652 aDestDrawTarget
, aSourceBlur
,
653 RectWithEdgesTRBL(aDstInner
.Y(), aDstOuter
.XMost(), aDstInner
.YMost(),
655 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcOuter
.XMost(), aSrcInner
.YMost(),
658 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
659 RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.XMost(),
660 aDstOuter
.YMost(), aDstInner
.X()),
661 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcInner
.XMost(),
662 aSrcOuter
.YMost(), aSrcInner
.X()),
667 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
668 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.XMost(),
669 aDstInner
.YMost(), aDstInner
.X()),
670 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.XMost(),
671 aSrcInner
.YMost(), aSrcInner
.X()),
676 static void DrawMirroredRect(DrawTarget
* aDT
, SourceSurface
* aSurface
,
677 const Rect
& aDest
, const Point
& aSrc
,
678 Float aScaleX
, Float aScaleY
) {
679 SurfacePattern
pattern(
680 aSurface
, ExtendMode::CLAMP
,
681 Matrix::Scaling(aScaleX
, aScaleY
)
683 .PostTranslate(aScaleX
< 0 ? aDest
.XMost() : aDest
.X(),
684 aScaleY
< 0 ? aDest
.YMost() : aDest
.Y()));
685 aDT
->FillRect(aDest
, pattern
);
688 static void DrawMirroredBoxShadow(DrawTarget
* aDT
, SourceSurface
* aSurface
,
689 const Rect
& aDestRect
) {
690 Point
center(ceil(aDestRect
.X() + aDestRect
.Width() / 2),
691 ceil(aDestRect
.Y() + aDestRect
.Height() / 2));
692 Rect
topLeft(aDestRect
.X(), aDestRect
.Y(), center
.x
- aDestRect
.X(),
693 center
.y
- aDestRect
.Y());
694 Rect
bottomRight(topLeft
.BottomRight(), aDestRect
.Size() - topLeft
.Size());
695 Rect
topRight(bottomRight
.X(), topLeft
.Y(), bottomRight
.Width(),
697 Rect
bottomLeft(topLeft
.X(), bottomRight
.Y(), topLeft
.Width(),
698 bottomRight
.Height());
699 DrawMirroredRect(aDT
, aSurface
, topLeft
, Point(), 1, 1);
700 DrawMirroredRect(aDT
, aSurface
, topRight
, Point(), -1, 1);
701 DrawMirroredRect(aDT
, aSurface
, bottomLeft
, Point(), 1, -1);
702 DrawMirroredRect(aDT
, aSurface
, bottomRight
, Point(), -1, -1);
705 static void DrawMirroredCorner(DrawTarget
* aDT
, SourceSurface
* aSurface
,
706 const Rect
& aDest
, const Point
& aSrc
,
707 const Rect
& aSkipRect
, Float aScaleX
,
709 if (aSkipRect
.Contains(aDest
)) {
713 DrawMirroredRect(aDT
, aSurface
, aDest
, aSrc
, aScaleX
, aScaleY
);
716 static void RepeatOrStretchMirroredSurface(DrawTarget
* aDT
,
717 SourceSurface
* aSurface
,
718 const Rect
& aDest
, const Rect
& aSrc
,
719 const Rect
& aSkipRect
, Float aScaleX
,
721 if (aSkipRect
.Contains(aDest
)) {
725 if (ShouldStretchSurface(aDT
, aSurface
)) {
726 aScaleX
*= aDest
.Width() / aSrc
.Width();
727 aScaleY
*= aDest
.Height() / aSrc
.Height();
728 DrawMirroredRect(aDT
, aSurface
, aDest
, aSrc
.TopLeft(), aScaleX
, aScaleY
);
732 SurfacePattern
pattern(
733 aSurface
, ExtendMode::REPEAT
,
734 Matrix::Scaling(aScaleX
, aScaleY
)
735 .PreTranslate(-aSrc
.TopLeft())
736 .PostTranslate(aScaleX
< 0 ? aDest
.XMost() : aDest
.X(),
737 aScaleY
< 0 ? aDest
.YMost() : aDest
.Y()),
738 SamplingFilter::GOOD
, RoundedToInt(aSrc
));
739 aDT
->FillRect(aDest
, pattern
);
742 static void DrawMirroredMinBoxShadow(
743 DrawTarget
* aDestDrawTarget
, SourceSurface
* aSourceBlur
,
744 const Rect
& aDstOuter
, const Rect
& aDstInner
, const Rect
& aSrcOuter
,
745 const Rect
& aSrcInner
, const Rect
& aSkipRect
, bool aMiddle
= false) {
746 // Corners: top left, top right, bottom left, bottom right
747 // Compute quadrant bounds and then clip them to corners along
748 // dimensions where we need to stretch from min size.
749 Point
center(ceil(aDstOuter
.X() + aDstOuter
.Width() / 2),
750 ceil(aDstOuter
.Y() + aDstOuter
.Height() / 2));
751 Rect
topLeft(aDstOuter
.X(), aDstOuter
.Y(), center
.x
- aDstOuter
.X(),
752 center
.y
- aDstOuter
.Y());
753 Rect
bottomRight(topLeft
.BottomRight(), aDstOuter
.Size() - topLeft
.Size());
754 Rect
topRight(bottomRight
.X(), topLeft
.Y(), bottomRight
.Width(),
756 Rect
bottomLeft(topLeft
.X(), bottomRight
.Y(), topLeft
.Width(),
757 bottomRight
.Height());
759 // Check if the middle part has been minimized along each dimension.
760 // If so, those will be strecthed/drawn separately and need to be clipped out.
761 if (aSrcInner
.Width() == 1) {
762 topLeft
.SetRightEdge(aDstInner
.X());
763 topRight
.SetLeftEdge(aDstInner
.XMost());
764 bottomLeft
.SetRightEdge(aDstInner
.X());
765 bottomRight
.SetLeftEdge(aDstInner
.XMost());
767 if (aSrcInner
.Height() == 1) {
768 topLeft
.SetBottomEdge(aDstInner
.Y());
769 topRight
.SetBottomEdge(aDstInner
.Y());
770 bottomLeft
.SetTopEdge(aDstInner
.YMost());
771 bottomRight
.SetTopEdge(aDstInner
.YMost());
774 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, topLeft
, aSrcOuter
.TopLeft(),
776 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, topRight
,
777 aSrcOuter
.TopLeft(), aSkipRect
, -1, 1);
778 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, bottomLeft
,
779 aSrcOuter
.TopLeft(), aSkipRect
, 1, -1);
780 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, bottomRight
,
781 aSrcOuter
.TopLeft(), aSkipRect
, -1, -1);
783 // Edges: top, bottom, left, right
784 // Draw middle edges where they need to be stretched. The top and left
785 // sections that are part of the top-left quadrant will be mirrored to
786 // the bottom and right sections, respectively.
787 if (aSrcInner
.Width() == 1) {
788 Rect dstTop
= RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.XMost(),
789 aDstInner
.Y(), aDstInner
.X());
790 Rect srcTop
= RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
791 aSrcInner
.Y(), aSrcInner
.X());
792 Rect dstBottom
= RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.XMost(),
793 aDstOuter
.YMost(), aDstInner
.X());
794 Rect srcBottom
= RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
795 aSrcInner
.Y(), aSrcInner
.X());
796 // If we only need to stretch along the X axis and we're drawing
797 // the middle section, just sample all the way to the center of the
798 // source on the Y axis to avoid extra draw calls.
799 if (aMiddle
&& aSrcInner
.Height() != 1) {
800 dstTop
.SetBottomEdge(center
.y
);
801 srcTop
.SetHeight(dstTop
.Height());
802 dstBottom
.SetTopEdge(dstTop
.YMost());
803 srcBottom
.SetHeight(dstBottom
.Height());
805 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstTop
, srcTop
,
807 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstBottom
,
808 srcBottom
, aSkipRect
, 1, -1);
811 if (aSrcInner
.Height() == 1) {
812 Rect dstLeft
= RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.X(),
813 aDstInner
.YMost(), aDstOuter
.X());
814 Rect srcLeft
= RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
815 aSrcInner
.YMost(), aSrcOuter
.X());
816 Rect dstRight
= RectWithEdgesTRBL(aDstInner
.Y(), aDstOuter
.XMost(),
817 aDstInner
.YMost(), aDstInner
.XMost());
818 Rect srcRight
= RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
819 aSrcInner
.YMost(), aSrcOuter
.X());
820 // Only stretching on Y axis, so sample source to the center of the X axis.
821 if (aMiddle
&& aSrcInner
.Width() != 1) {
822 dstLeft
.SetRightEdge(center
.x
);
823 srcLeft
.SetWidth(dstLeft
.Width());
824 dstRight
.SetLeftEdge(dstLeft
.XMost());
825 srcRight
.SetWidth(dstRight
.Width());
827 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstLeft
,
828 srcLeft
, aSkipRect
, 1, 1);
829 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstRight
,
830 srcRight
, aSkipRect
, -1, 1);
833 // If we need to stretch along both dimensions, then the middle part
834 // must be drawn separately.
835 if (aMiddle
&& aSrcInner
.Width() == 1 && aSrcInner
.Height() == 1) {
836 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
837 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.XMost(),
838 aDstInner
.YMost(), aDstInner
.X()),
839 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.XMost(),
840 aSrcInner
.YMost(), aSrcInner
.X()),
846 * We draw a blurred a rectangle by only blurring a smaller rectangle and
847 * splitting the rectangle into 9 parts.
848 * First, a small minimum source rect is calculated and used to create a blur
849 * mask since the actual blurring itself is expensive. Next, we use the mask
850 * with the given shadow color to create a minimally-sized box shadow of the
851 * right color. Finally, we cut out the 9 parts from the box-shadow source and
852 * paint each part in the right place, stretching the non-corner parts to fill
853 * the space between the corners.
857 void gfxAlphaBoxBlur::BlurRectangle(gfxContext
* aDestinationCtx
,
858 const gfxRect
& aRect
,
859 const RectCornerRadii
* aCornerRadii
,
860 const gfxPoint
& aBlurStdDev
,
861 const sRGBColor
& aShadowColor
,
862 const gfxRect
& aDirtyRect
,
863 const gfxRect
& aSkipRect
) {
864 if (!RectIsInt32Safe(ToRect(aRect
))) {
868 IntSize blurRadius
= CalculateBlurRadius(aBlurStdDev
);
869 bool mirrorCorners
= !aCornerRadii
|| aCornerRadii
->AreRadiiSame();
871 IntRect rect
= RoundedToInt(ToRect(aRect
));
872 IntMargin blurMargin
;
875 RefPtr
<SourceSurface
> boxShadow
=
876 GetBlur(aDestinationCtx
, rect
.Size(), blurRadius
, aCornerRadii
,
877 aShadowColor
, mirrorCorners
, blurMargin
, slice
, minSize
);
882 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
883 destDrawTarget
->PushClipRect(ToRect(aDirtyRect
));
885 // Copy the right parts from boxShadow into destDrawTarget. The middle parts
886 // will be stretched, border-image style.
888 Rect
srcOuter(Point(blurMargin
.left
, blurMargin
.top
), Size(minSize
));
889 Rect
srcInner(srcOuter
);
890 srcOuter
.Inflate(Margin(blurMargin
));
891 srcInner
.Deflate(Margin(slice
));
895 dstOuter
.Inflate(Margin(blurMargin
));
896 dstInner
.Deflate(Margin(slice
));
898 Rect skipRect
= ToRect(aSkipRect
);
900 if (minSize
== rect
.Size()) {
901 // The target rect is smaller than the minimal size so just draw the surface
903 DrawMirroredBoxShadow(destDrawTarget
, boxShadow
, dstOuter
);
905 destDrawTarget
->DrawSurface(boxShadow
, dstOuter
, srcOuter
);
909 DrawMirroredMinBoxShadow(destDrawTarget
, boxShadow
, dstOuter
, dstInner
,
910 srcOuter
, srcInner
, skipRect
, true);
912 DrawMinBoxShadow(destDrawTarget
, boxShadow
, dstOuter
, dstInner
, srcOuter
,
913 srcInner
, skipRect
, true);
917 // A note about anti-aliasing and seems between adjacent parts:
918 // We don't explicitly disable anti-aliasing in the DrawSurface calls above,
919 // so if there's a transform on destDrawTarget that is not pixel-aligned,
920 // there will be seams between adjacent parts of the box-shadow. It's hard to
921 // avoid those without the use of an intermediate surface.
922 // You might think that we could avoid those by just turning off AA, but there
923 // is a problem with that: Box-shadow rendering needs to clip out the
924 // element's border box, and we'd like that clip to have anti-aliasing -
925 // especially if the element has rounded corners! So we can't do that unless
926 // we have a way to say "Please anti-alias the clip, but don't antialias the
927 // destination rect of the DrawSurface call".
929 destDrawTarget
->PopClip();
932 static already_AddRefed
<Path
> GetBoxShadowInsetPath(
933 DrawTarget
* aDrawTarget
, const Rect aOuterRect
, const Rect aInnerRect
,
934 const RectCornerRadii
* aInnerClipRadii
) {
936 * We create an inset path by having two rects.
938 * -----------------------
939 * | ________________ |
942 * | ------------------ |
943 * |_____________________|
945 * The outer rect and the inside rect. The path
946 * creates a frame around the content where we draw the inset shadow.
948 RefPtr
<PathBuilder
> builder
=
949 aDrawTarget
->CreatePathBuilder(FillRule::FILL_EVEN_ODD
);
950 AppendRectToPath(builder
, aOuterRect
, true);
952 if (aInnerClipRadii
) {
953 AppendRoundedRectToPath(builder
, aInnerRect
, *aInnerClipRadii
, false);
955 AppendRectToPath(builder
, aInnerRect
, false);
957 return builder
->Finish();
960 static void FillDestinationPath(
961 gfxContext
* aDestinationCtx
, const Rect
& aDestinationRect
,
962 const Rect
& aShadowClipRect
, const sRGBColor
& aShadowColor
,
963 const RectCornerRadii
* aInnerClipRadii
= nullptr) {
964 // When there is no blur radius, fill the path onto the destination
966 aDestinationCtx
->SetColor(aShadowColor
);
967 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
968 RefPtr
<Path
> shadowPath
= GetBoxShadowInsetPath(
969 destDrawTarget
, aDestinationRect
, aShadowClipRect
, aInnerClipRadii
);
971 aDestinationCtx
->SetPath(shadowPath
);
972 aDestinationCtx
->Fill();
975 static void CacheInsetBlur(const IntSize
& aMinOuterSize
,
976 const IntSize
& aMinInnerSize
,
977 const IntSize
& aBlurRadius
,
978 const RectCornerRadii
* aCornerRadii
,
979 const sRGBColor
& aShadowColor
,
980 BackendType aBackendType
,
981 SourceSurface
* aBoxShadow
) {
982 bool isInsetBlur
= true;
983 BlurCacheKey
key(aMinOuterSize
, aMinInnerSize
, aBlurRadius
, aCornerRadii
,
984 aShadowColor
, isInsetBlur
, aBackendType
);
985 IntMargin
blurMargin(0, 0, 0, 0);
987 gBlurCache
->RegisterEntry(
988 MakeUnique
<BlurCacheData
>(aBoxShadow
, blurMargin
, std::move(key
)));
991 already_AddRefed
<SourceSurface
> gfxAlphaBoxBlur::GetInsetBlur(
992 const Rect
& aOuterRect
, const Rect
& aWhitespaceRect
, bool aIsDestRect
,
993 const sRGBColor
& aShadowColor
, const IntSize
& aBlurRadius
,
994 const RectCornerRadii
* aInnerClipRadii
, DrawTarget
* aDestDrawTarget
,
995 bool aMirrorCorners
) {
997 gBlurCache
= new BlurCache();
1000 IntSize outerSize
= IntSize::Truncate(aOuterRect
.Size());
1001 IntSize whitespaceSize
= IntSize::Truncate(aWhitespaceRect
.Size());
1003 BlurCacheData
* cached
= gBlurCache
->LookupInsetBoxShadow(
1004 outerSize
, whitespaceSize
, aBlurRadius
, aInnerClipRadii
, aShadowColor
,
1005 aDestDrawTarget
->GetBackendType());
1007 // So we don't forget the actual cached blur
1008 RefPtr
<SourceSurface
> cachedBlur
= cached
->mBlur
;
1009 return cachedBlur
.forget();
1013 // If we can do a min rect, the whitespace rect will be expanded in Init to
1015 Rect blurRect
= aIsDestRect
? aOuterRect
: aWhitespaceRect
;
1016 // If mirroring corners, we only need to draw the top-left quadrant.
1017 // Use ceil to preserve the remaining 1x1 middle area for minimized box
1019 if (aMirrorCorners
) {
1020 blurRect
.SizeTo(ceil(blurRect
.Width() * 0.5f
),
1021 ceil(blurRect
.Height() * 0.5f
));
1023 IntSize
zeroSpread(0, 0);
1024 RefPtr
<DrawTarget
> minDrawTarget
=
1025 InitDrawTarget(aDestDrawTarget
, blurRect
, zeroSpread
, aBlurRadius
);
1026 if (!minDrawTarget
) {
1030 // This is really annoying. When we create the AlphaBoxBlur, the DrawTarget
1031 // has a translation applied to it that is the topLeft point. This is actually
1032 // the rect we gave it plus the blur radius. The rects we give this for the
1033 // outer and whitespace rects are based at (0, 0). We could either translate
1034 // those rects when we don't have a destination rect or ignore the translation
1035 // when using the dest rect. The dest rects layout gives us expect this
1038 minDrawTarget
->SetTransform(Matrix());
1041 // Fill in the path between the inside white space / outer rects
1042 // NOT the inner frame
1043 RefPtr
<Path
> maskPath
= GetBoxShadowInsetPath(
1044 minDrawTarget
, aOuterRect
, aWhitespaceRect
, aInnerClipRadii
);
1046 ColorPattern
black(DeviceColor::MaskOpaqueBlack());
1047 minDrawTarget
->Fill(maskPath
, black
);
1049 // Blur and fill in with the color we actually wanted
1050 RefPtr
<SourceSurface
> minInsetBlur
= DoBlur(&aShadowColor
);
1051 if (!minInsetBlur
) {
1055 if (RefPtr
<SourceSurface
> opt
=
1056 aDestDrawTarget
->OptimizeSourceSurface(minInsetBlur
)) {
1061 CacheInsetBlur(outerSize
, whitespaceSize
, aBlurRadius
, aInnerClipRadii
,
1062 aShadowColor
, aDestDrawTarget
->GetBackendType(),
1066 return minInsetBlur
.forget();
1070 * We create our minimal rect with 2 rects.
1071 * The first is the inside whitespace rect, that is "cut out"
1072 * from the box. This is (1). This must be the size
1073 * of the blur radius + corner radius so we can have a big enough
1076 * The second (2) is one blur radius surrounding the inner
1077 * frame of (1). This is the amount of blur space required
1078 * to get a proper blend.
1081 * W = one blur + corner radii - known as inner margin
1082 * ___________________________________
1085 * | (2) | (1) | (2) |
1090 * |________________________________|
1092 static void GetBlurMargins(const RectCornerRadii
* aInnerClipRadii
,
1093 const IntSize
& aBlurRadius
, Margin
& aOutBlurMargin
,
1094 Margin
& aOutInnerMargin
) {
1095 Size
cornerSize(0, 0);
1096 if (aInnerClipRadii
) {
1097 const RectCornerRadii
& corners
= *aInnerClipRadii
;
1098 for (const auto i
: mozilla::AllPhysicalCorners()) {
1099 cornerSize
.width
= std::max(cornerSize
.width
, corners
[i
].width
);
1100 cornerSize
.height
= std::max(cornerSize
.height
, corners
[i
].height
);
1104 // Only the inside whitespace size cares about the border radius size.
1105 // Outer sizes only care about blur.
1106 IntSize margin
= IntSize::Ceil(cornerSize
) + aBlurRadius
;
1108 aOutInnerMargin
.SizeTo(margin
.height
, margin
.width
, margin
.height
,
1110 aOutBlurMargin
.SizeTo(aBlurRadius
.height
, aBlurRadius
.width
,
1111 aBlurRadius
.height
, aBlurRadius
.width
);
1114 static bool GetInsetBoxShadowRects(const Margin
& aBlurMargin
,
1115 const Margin
& aInnerMargin
,
1116 const Rect
& aShadowClipRect
,
1117 const Rect
& aDestinationRect
,
1118 Rect
& aOutWhitespaceRect
,
1119 Rect
& aOutOuterRect
) {
1120 // We always copy (2 * blur radius) + corner radius worth of data to the
1121 // destination rect This covers the blend of the path + the actual blur Need
1122 // +1 so that we copy the edges correctly as we'll copy over the min box
1123 // shadow corners then the +1 for the edges between Note, the (x,y)
1124 // coordinates are from the blur margin since the frame outside the whitespace
1125 // rect is 1 blur radius extra space.
1126 Rect
insideWhiteSpace(aBlurMargin
.left
, aBlurMargin
.top
,
1127 aInnerMargin
.LeftRight() + 1,
1128 aInnerMargin
.TopBottom() + 1);
1130 // If the inner white space rect is larger than the shadow clip rect
1131 // our approach does not work as we'll just copy one corner
1132 // and cover the destination. In those cases, fallback to the destination rect
1133 bool useDestRect
= (aShadowClipRect
.Width() <= aInnerMargin
.LeftRight()) ||
1134 (aShadowClipRect
.Height() <= aInnerMargin
.TopBottom());
1137 aOutWhitespaceRect
= aShadowClipRect
;
1138 aOutOuterRect
= aDestinationRect
;
1140 aOutWhitespaceRect
= insideWhiteSpace
;
1141 aOutOuterRect
= aOutWhitespaceRect
;
1142 aOutOuterRect
.Inflate(aBlurMargin
);
1148 void gfxAlphaBoxBlur::BlurInsetBox(
1149 gfxContext
* aDestinationCtx
, const Rect
& aDestinationRect
,
1150 const Rect
& aShadowClipRect
, const IntSize
& aBlurRadius
,
1151 const sRGBColor
& aShadowColor
, const RectCornerRadii
* aInnerClipRadii
,
1152 const Rect
& aSkipRect
, const Point
& aShadowOffset
) {
1153 if ((aBlurRadius
.width
== 0 && aBlurRadius
.height
== 0) ||
1154 aShadowClipRect
.IsEmpty()) {
1155 FillDestinationPath(aDestinationCtx
, aDestinationRect
, aShadowClipRect
,
1156 aShadowColor
, aInnerClipRadii
);
1160 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
1164 GetBlurMargins(aInnerClipRadii
, aBlurRadius
, blurMargin
, innerMargin
);
1166 Rect whitespaceRect
;
1169 GetInsetBoxShadowRects(blurMargin
, innerMargin
, aShadowClipRect
,
1170 aDestinationRect
, whitespaceRect
, outerRect
);
1172 // Check that the inset margin between the outer and whitespace rects is
1173 // symmetric, and that all corner radii are the same, in which case the blur
1175 Margin checkMargin
= outerRect
- whitespaceRect
;
1176 bool mirrorCorners
= checkMargin
.left
== checkMargin
.right
&&
1177 checkMargin
.top
== checkMargin
.bottom
&&
1178 (!aInnerClipRadii
|| aInnerClipRadii
->AreRadiiSame());
1179 RefPtr
<SourceSurface
> minBlur
=
1180 GetInsetBlur(outerRect
, whitespaceRect
, useDestRect
, aShadowColor
,
1181 aBlurRadius
, aInnerClipRadii
, destDrawTarget
, mirrorCorners
);
1187 Rect destBlur
= aDestinationRect
;
1188 destBlur
.Inflate(blurMargin
);
1189 if (mirrorCorners
) {
1190 DrawMirroredBoxShadow(destDrawTarget
, minBlur
.get(), destBlur
);
1192 Rect
srcBlur(Point(0, 0), Size(minBlur
->GetSize()));
1193 MOZ_ASSERT(RoundedOut(srcBlur
).Size() == RoundedOut(destBlur
).Size());
1194 destDrawTarget
->DrawSurface(minBlur
, destBlur
, srcBlur
);
1197 Rect
srcOuter(outerRect
);
1198 Rect
srcInner(srcOuter
);
1199 srcInner
.Deflate(blurMargin
); // The outer color fill
1200 srcInner
.Deflate(innerMargin
); // The inner whitespace
1202 // The shadow clip rect already takes into account the spread radius
1203 Rect
outerFillRect(aShadowClipRect
);
1204 outerFillRect
.Inflate(blurMargin
);
1205 FillDestinationPath(aDestinationCtx
, aDestinationRect
, outerFillRect
,
1208 // Inflate once for the frame around the whitespace
1209 Rect
destRect(aShadowClipRect
);
1210 destRect
.Inflate(blurMargin
);
1212 // Deflate for the blurred in white space
1213 Rect
destInnerRect(aShadowClipRect
);
1214 destInnerRect
.Deflate(innerMargin
);
1216 if (mirrorCorners
) {
1217 DrawMirroredMinBoxShadow(destDrawTarget
, minBlur
, destRect
, destInnerRect
,
1218 srcOuter
, srcInner
, aSkipRect
);
1220 DrawMinBoxShadow(destDrawTarget
, minBlur
, destRect
, destInnerRect
,
1221 srcOuter
, srcInner
, aSkipRect
);