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 "mozilla/SystemGroup.h"
16 #include "nsExpirationTracker.h"
17 #include "nsClassHashtable.h"
22 using namespace mozilla
;
23 using namespace mozilla::gfx
;
25 gfxAlphaBoxBlur::~gfxAlphaBoxBlur() {}
27 already_AddRefed
<gfxContext
> gfxAlphaBoxBlur::Init(gfxContext
* aDestinationCtx
,
29 const IntSize
& aSpreadRadius
,
30 const IntSize
& aBlurRadius
,
31 const gfxRect
* aDirtyRect
,
32 const gfxRect
* aSkipRect
,
33 bool aUseHardwareAccel
) {
34 DrawTarget
* refDT
= aDestinationCtx
->GetDrawTarget();
35 Maybe
<Rect
> dirtyRect
= aDirtyRect
? Some(ToRect(*aDirtyRect
)) : Nothing();
36 Maybe
<Rect
> skipRect
= aSkipRect
? Some(ToRect(*aSkipRect
)) : Nothing();
37 RefPtr
<DrawTarget
> dt
= InitDrawTarget(
38 refDT
, ToRect(aRect
), aSpreadRadius
, aBlurRadius
,
39 dirtyRect
.ptrOr(nullptr), skipRect
.ptrOr(nullptr), aUseHardwareAccel
);
44 RefPtr
<gfxContext
> context
= gfxContext::CreateOrNull(dt
);
45 MOZ_ASSERT(context
); // already checked for target above
46 context
->SetMatrix(Matrix::Translation(-mBlur
.GetRect().TopLeft()));
47 return context
.forget();
50 already_AddRefed
<DrawTarget
> gfxAlphaBoxBlur::InitDrawTarget(
51 const DrawTarget
* aReferenceDT
, const Rect
& aRect
,
52 const IntSize
& aSpreadRadius
, const IntSize
& aBlurRadius
,
53 const Rect
* aDirtyRect
, const Rect
* aSkipRect
, bool aUseHardwareAccel
) {
54 mBlur
.Init(aRect
, aSpreadRadius
, aBlurRadius
, aDirtyRect
, aSkipRect
);
55 size_t blurDataSize
= mBlur
.GetSurfaceAllocationSize();
56 if (blurDataSize
== 0) {
60 BackendType backend
= aReferenceDT
->GetBackendType();
62 // Check if the backend has an accelerated DrawSurfaceWithShadow.
63 // Currently, only D2D1.1 supports this.
64 // Otherwise, DrawSurfaceWithShadow only supports square blurs without spread.
65 // When blurring small draw targets such as short spans text, the cost of
66 // creating and flushing an accelerated draw target may exceed the speedup
67 // gained from the faster blur. It's up to the users of this blur
68 // to determine whether they want to use hardware acceleration.
69 if (aBlurRadius
.IsSquare() && aSpreadRadius
.IsEmpty() && aUseHardwareAccel
&&
70 backend
== BackendType::DIRECT2D1_1
) {
74 if (aReferenceDT
->IsCaptureDT()) {
76 mDrawTarget
= Factory::CreateCaptureDrawTarget(backend
, mBlur
.GetSize(),
79 mDrawTarget
= Factory::CreateCaptureDrawTargetForData(
80 backend
, mBlur
.GetSize(), SurfaceFormat::A8
, mBlur
.GetStride(),
83 } else if (mAccelerated
) {
84 // Note: CreateShadowDrawTarget is only implemented for Cairo, so we don't
85 // care about mimicking this in the DrawTargetCapture case.
86 mDrawTarget
= aReferenceDT
->CreateShadowDrawTarget(
87 mBlur
.GetSize(), SurfaceFormat::A8
,
88 AlphaBoxBlur::CalculateBlurSigma(aBlurRadius
.width
));
90 // See Bug 1526045 - this is to force DT initialization.
91 mDrawTarget
->ClearRect(gfx::Rect());
94 // Make an alpha-only surface to draw on. We will play with the data after
95 // everything is drawn to create a blur effect.
96 // This will be freed when the DrawTarget dies
97 mData
= static_cast<uint8_t*>(calloc(1, blurDataSize
));
102 Factory::DoesBackendSupportDataDrawtarget(backend
)
103 ? Factory::CreateDrawTargetForData(backend
, mData
, mBlur
.GetSize(),
106 : gfxPlatform::CreateDrawTargetForData(
107 mData
, mBlur
.GetSize(), mBlur
.GetStride(), SurfaceFormat::A8
);
110 if (!mDrawTarget
|| !mDrawTarget
->IsValid()) {
119 mDrawTarget
->AddUserData(reinterpret_cast<UserDataKey
*>(mDrawTarget
.get()),
123 mDrawTarget
->SetTransform(Matrix::Translation(-mBlur
.GetRect().TopLeft()));
124 return do_AddRef(mDrawTarget
);
127 already_AddRefed
<SourceSurface
> gfxAlphaBoxBlur::DoBlur(
128 const Color
* aShadowColor
, IntPoint
* aOutTopLeft
) {
130 *aOutTopLeft
= mBlur
.GetRect().TopLeft();
133 RefPtr
<SourceSurface
> blurMask
;
136 blurMask
= mDrawTarget
->Snapshot();
137 } else if (mAccelerated
) {
138 blurMask
= mDrawTarget
->Snapshot();
139 RefPtr
<DrawTarget
> blurDT
= mDrawTarget
->CreateSimilarDrawTarget(
140 blurMask
->GetSize(), SurfaceFormat::A8
);
144 blurDT
->DrawSurfaceWithShadow(
145 blurMask
, Point(0, 0), Color(1, 1, 1), Point(0, 0),
146 AlphaBoxBlur::CalculateBlurSigma(mBlur
.GetBlurRadius().width
),
147 CompositionOp::OP_OVER
);
148 blurMask
= blurDT
->Snapshot();
149 } else if (mDrawTarget
->IsCaptureDT()) {
150 mDrawTarget
->Blur(mBlur
);
151 blurMask
= mDrawTarget
->Snapshot();
155 return blurMask
.forget();
158 RefPtr
<DrawTarget
> shadowDT
= mDrawTarget
->CreateSimilarDrawTarget(
159 blurMask
->GetSize(), SurfaceFormat::B8G8R8A8
);
163 ColorPattern
shadowColor(ToDeviceColor(*aShadowColor
));
164 shadowDT
->MaskSurface(shadowColor
, blurMask
, Point(0, 0));
166 return shadowDT
->Snapshot();
169 void gfxAlphaBoxBlur::Paint(gfxContext
* aDestinationCtx
) {
170 if ((mDrawTarget
&& !mDrawTarget
->IsCaptureDT()) && !mAccelerated
&& !mData
) {
174 DrawTarget
* dest
= aDestinationCtx
->GetDrawTarget();
176 NS_WARNING("Blurring not supported for Thebes contexts!");
180 RefPtr
<gfxPattern
> thebesPat
= aDestinationCtx
->GetPattern();
181 Pattern
* pat
= thebesPat
->GetPattern(dest
, nullptr);
183 NS_WARNING("Failed to get pattern for blur!");
188 RefPtr
<SourceSurface
> mask
= DoBlur(nullptr, &topLeft
);
190 NS_ERROR("Failed to create mask!");
194 // Avoid a semi-expensive clip operation if we can, otherwise
195 // clip to the dirty rect
196 Rect
* dirtyRect
= mBlur
.GetDirtyRect();
198 dest
->PushClipRect(*dirtyRect
);
201 Matrix oldTransform
= dest
->GetTransform();
202 Matrix newTransform
= oldTransform
;
203 newTransform
.PreTranslate(topLeft
);
204 dest
->SetTransform(newTransform
);
206 dest
->MaskSurface(*pat
, mask
, Point(0, 0));
208 dest
->SetTransform(oldTransform
);
215 IntSize
gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint
& aStd
) {
216 mozilla::gfx::Point
std(Float(aStd
.x
), Float(aStd
.y
));
217 IntSize size
= AlphaBoxBlur::CalculateBlurRadius(std
);
218 return IntSize(size
.width
, size
.height
);
221 struct BlurCacheKey
: public PLDHashEntryHdr
{
222 typedef const BlurCacheKey
& KeyType
;
223 typedef const BlurCacheKey
* KeyTypePointer
;
224 enum { ALLOW_MEMMOVE
= true };
229 BackendType mBackend
;
230 RectCornerRadii mCornerRadii
;
233 // Only used for inset blurs
234 IntSize mInnerMinSize
;
236 BlurCacheKey(const IntSize
& aMinSize
, const IntSize
& aBlurRadius
,
237 const RectCornerRadii
* aCornerRadii
, const Color
& aShadowColor
,
238 BackendType aBackendType
)
239 : BlurCacheKey(aMinSize
, IntSize(0, 0), aBlurRadius
, aCornerRadii
,
240 aShadowColor
, false, aBackendType
) {}
242 explicit BlurCacheKey(const BlurCacheKey
* aOther
)
243 : mMinSize(aOther
->mMinSize
),
244 mBlurRadius(aOther
->mBlurRadius
),
245 mShadowColor(aOther
->mShadowColor
),
246 mBackend(aOther
->mBackend
),
247 mCornerRadii(aOther
->mCornerRadii
),
248 mIsInset(aOther
->mIsInset
),
249 mInnerMinSize(aOther
->mInnerMinSize
) {}
251 explicit BlurCacheKey(const IntSize
& aOuterMinSize
,
252 const IntSize
& aInnerMinSize
,
253 const IntSize
& aBlurRadius
,
254 const RectCornerRadii
* aCornerRadii
,
255 const Color
& aShadowColor
, bool aIsInset
,
256 BackendType aBackendType
)
257 : mMinSize(aOuterMinSize
),
258 mBlurRadius(aBlurRadius
),
259 mShadowColor(aShadowColor
),
260 mBackend(aBackendType
),
261 mCornerRadii(aCornerRadii
? *aCornerRadii
: RectCornerRadii()),
263 mInnerMinSize(aInnerMinSize
) {}
265 BlurCacheKey(BlurCacheKey
&&) = default;
267 static PLDHashNumber
HashKey(const KeyTypePointer aKey
) {
268 PLDHashNumber hash
= 0;
269 hash
= AddToHash(hash
, aKey
->mMinSize
.width
, aKey
->mMinSize
.height
);
270 hash
= AddToHash(hash
, aKey
->mBlurRadius
.width
, aKey
->mBlurRadius
.height
);
273 hash
, HashBytes(&aKey
->mShadowColor
.r
, sizeof(aKey
->mShadowColor
.r
)));
275 hash
, HashBytes(&aKey
->mShadowColor
.g
, sizeof(aKey
->mShadowColor
.g
)));
277 hash
, HashBytes(&aKey
->mShadowColor
.b
, sizeof(aKey
->mShadowColor
.b
)));
279 hash
, HashBytes(&aKey
->mShadowColor
.a
, sizeof(aKey
->mShadowColor
.a
)));
281 for (int i
= 0; i
< 4; i
++) {
282 hash
= AddToHash(hash
, aKey
->mCornerRadii
[i
].width
,
283 aKey
->mCornerRadii
[i
].height
);
286 hash
= AddToHash(hash
, (uint32_t)aKey
->mBackend
);
288 if (aKey
->mIsInset
) {
289 hash
= AddToHash(hash
, aKey
->mInnerMinSize
.width
,
290 aKey
->mInnerMinSize
.height
);
295 bool KeyEquals(KeyTypePointer aKey
) const {
296 if (aKey
->mMinSize
== mMinSize
&& aKey
->mBlurRadius
== mBlurRadius
&&
297 aKey
->mCornerRadii
== mCornerRadii
&&
298 aKey
->mShadowColor
== mShadowColor
&& aKey
->mBackend
== mBackend
) {
300 return (mInnerMinSize
== aKey
->mInnerMinSize
);
309 static KeyTypePointer
KeyToPointer(KeyType aKey
) { return &aKey
; }
313 * This class is what is cached. It need to be allocated in an object separated
314 * to the cache entry to be able to be tracked by the nsExpirationTracker.
316 struct BlurCacheData
{
317 BlurCacheData(SourceSurface
* aBlur
, const IntMargin
& aBlurMargin
,
319 : mBlur(aBlur
), mBlurMargin(aBlurMargin
), mKey(std::move(aKey
)) {}
321 BlurCacheData(BlurCacheData
&& aOther
) = default;
323 nsExpirationState
* GetExpirationState() { return &mExpirationState
; }
325 nsExpirationState mExpirationState
;
326 RefPtr
<SourceSurface
> mBlur
;
327 IntMargin mBlurMargin
;
332 * This class implements a cache with no maximum size, that retains the
333 * SourceSurfaces used to draw the blurs.
335 * An entry stays in the cache as long as it is used often.
337 class BlurCache final
: public nsExpirationTracker
<BlurCacheData
, 4> {
340 : nsExpirationTracker
<BlurCacheData
, 4>(
341 GENERATION_MS
, "BlurCache",
342 SystemGroup::EventTargetFor(TaskCategory::Other
)) {}
344 virtual void NotifyExpired(BlurCacheData
* aObject
) override
{
345 RemoveObject(aObject
);
346 mHashEntries
.Remove(aObject
->mKey
);
349 BlurCacheData
* Lookup(const IntSize
& aMinSize
, const IntSize
& aBlurRadius
,
350 const RectCornerRadii
* aCornerRadii
,
351 const Color
& aShadowColor
, BackendType aBackendType
) {
352 BlurCacheData
* blur
= mHashEntries
.Get(BlurCacheKey(
353 aMinSize
, aBlurRadius
, aCornerRadii
, aShadowColor
, aBackendType
));
361 BlurCacheData
* LookupInsetBoxShadow(const IntSize
& aOuterMinSize
,
362 const IntSize
& aInnerMinSize
,
363 const IntSize
& aBlurRadius
,
364 const RectCornerRadii
* aCornerRadii
,
365 const Color
& aShadowColor
,
366 BackendType aBackendType
) {
367 bool insetBoxShadow
= true;
368 BlurCacheKey
key(aOuterMinSize
, aInnerMinSize
, aBlurRadius
, aCornerRadii
,
369 aShadowColor
, insetBoxShadow
, aBackendType
);
370 BlurCacheData
* blur
= mHashEntries
.Get(key
);
378 // Returns true if we successfully register the blur in the cache, false
380 bool RegisterEntry(BlurCacheData
* aValue
) {
381 nsresult rv
= AddObject(aValue
);
383 // We are OOM, and we cannot track this object. We don't want stall
384 // entries in the hash table (since the expiration tracker is responsible
385 // for removing the cache entries), so we avoid putting that entry in the
386 // table, which is a good things considering we are short on memory
387 // anyway, we probably don't want to retain things.
390 mHashEntries
.Put(aValue
->mKey
, aValue
);
395 static const uint32_t GENERATION_MS
= 1000;
397 * FIXME use nsTHashtable to avoid duplicating the BlurCacheKey.
398 * https://bugzilla.mozilla.org/show_bug.cgi?id=761393#c47
400 nsClassHashtable
<BlurCacheKey
, BlurCacheData
> mHashEntries
;
403 static BlurCache
* gBlurCache
= nullptr;
405 static IntSize
ComputeMinSizeForShadowShape(const RectCornerRadii
* aCornerRadii
,
406 const IntSize
& aBlurRadius
,
407 IntMargin
& aOutSlice
,
408 const IntSize
& aRectSize
) {
409 Size
cornerSize(0, 0);
411 const RectCornerRadii
& corners
= *aCornerRadii
;
412 NS_FOR_CSS_FULL_CORNERS(i
) {
413 cornerSize
.width
= std::max(cornerSize
.width
, corners
[i
].width
);
414 cornerSize
.height
= std::max(cornerSize
.height
, corners
[i
].height
);
418 IntSize margin
= IntSize::Ceil(cornerSize
) + aBlurRadius
;
420 IntMargin(margin
.height
, margin
.width
, margin
.height
, margin
.width
);
422 IntSize
minSize(aOutSlice
.LeftRight() + 1, aOutSlice
.TopBottom() + 1);
424 // If aRectSize is smaller than minSize, the border-image approach won't
425 // work; there's no way to squeeze parts of the min box-shadow source
426 // image such that the result looks correct. So we need to adjust minSize
427 // in such a way that we can later draw it without stretching in the affected
428 // dimension. We also need to adjust "slice" to ensure that we're not trying
429 // to slice away more than we have.
430 if (aRectSize
.width
< minSize
.width
) {
431 minSize
.width
= aRectSize
.width
;
435 if (aRectSize
.height
< minSize
.height
) {
436 minSize
.height
= aRectSize
.height
;
438 aOutSlice
.bottom
= 0;
441 MOZ_ASSERT(aOutSlice
.LeftRight() <= minSize
.width
);
442 MOZ_ASSERT(aOutSlice
.TopBottom() <= minSize
.height
);
446 static void CacheBlur(DrawTarget
* aDT
, const IntSize
& aMinSize
,
447 const IntSize
& aBlurRadius
,
448 const RectCornerRadii
* aCornerRadii
,
449 const Color
& aShadowColor
, const IntMargin
& aBlurMargin
,
450 SourceSurface
* aBoxShadow
) {
451 BlurCacheKey
key(aMinSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
452 aDT
->GetBackendType());
453 BlurCacheData
* data
=
454 new BlurCacheData(aBoxShadow
, aBlurMargin
, std::move(key
));
455 if (!gBlurCache
->RegisterEntry(data
)) {
460 // Blurs a small surface and creates the colored box shadow.
461 static already_AddRefed
<SourceSurface
> CreateBoxShadow(
462 DrawTarget
* aDestDrawTarget
, const IntSize
& aMinSize
,
463 const RectCornerRadii
* aCornerRadii
, const IntSize
& aBlurRadius
,
464 const Color
& aShadowColor
, bool aMirrorCorners
, IntMargin
& aOutBlurMargin
) {
465 gfxAlphaBoxBlur blur
;
466 Rect
minRect(Point(0, 0), Size(aMinSize
));
467 Rect
blurRect(minRect
);
468 // If mirroring corners, we only need to draw the top-left quadrant.
469 // Use ceil to preserve the remaining 1x1 middle area for minimized box
471 if (aMirrorCorners
) {
472 blurRect
.SizeTo(ceil(blurRect
.Width() * 0.5f
),
473 ceil(blurRect
.Height() * 0.5f
));
475 IntSize
zeroSpread(0, 0);
476 RefPtr
<DrawTarget
> blurDT
=
477 blur
.InitDrawTarget(aDestDrawTarget
, blurRect
, zeroSpread
, aBlurRadius
);
482 ColorPattern
black(Color(0.f
, 0.f
, 0.f
, 1.f
));
485 RefPtr
<Path
> roundedRect
=
486 MakePathForRoundedRect(*blurDT
, minRect
, *aCornerRadii
);
487 blurDT
->Fill(roundedRect
, black
);
489 blurDT
->FillRect(minRect
, black
);
493 RefPtr
<SourceSurface
> result
= blur
.DoBlur(&aShadowColor
, &topLeft
);
498 // Since blurRect is at (0, 0), we can find the inflated margin by
499 // negating the new rect origin, which would have been negative if
500 // the rect was inflated.
501 aOutBlurMargin
= IntMargin(-topLeft
.y
, -topLeft
.x
, -topLeft
.y
, -topLeft
.x
);
503 return result
.forget();
506 static already_AddRefed
<SourceSurface
> GetBlur(
507 gfxContext
* aDestinationCtx
, const IntSize
& aRectSize
,
508 const IntSize
& aBlurRadius
, const RectCornerRadii
* aCornerRadii
,
509 const Color
& aShadowColor
, bool aMirrorCorners
, IntMargin
& aOutBlurMargin
,
510 IntMargin
& aOutSlice
, IntSize
& aOutMinSize
) {
512 gBlurCache
= new BlurCache();
515 IntSize minSize
= ComputeMinSizeForShadowShape(aCornerRadii
, aBlurRadius
,
516 aOutSlice
, aRectSize
);
518 // We can get seams using the min size rect when drawing to the destination
519 // rect if we have a non-pixel aligned destination transformation. In those
520 // cases, fallback to just rendering the destination rect. During printing, we
521 // record all the Moz 2d commands and replay them on the parent side with
522 // Cairo. Cairo printing uses StretchDIBits to stretch the surface. However,
523 // since our source image is only 1px for some parts, we make thousands of
524 // calls. Instead just render the blur ourself here as one image and send it
525 // over for printing.
526 // TODO: May need to change this with the blob renderer in WR since it also
528 Matrix destMatrix
= aDestinationCtx
->CurrentMatrix();
529 bool useDestRect
= !destMatrix
.IsRectilinear() ||
530 destMatrix
.HasNonIntegerTranslation() ||
531 aDestinationCtx
->GetDrawTarget()->IsRecording();
536 int32_t maxTextureSize
= gfxPlatform::MaxTextureSize();
537 if (minSize
.width
> maxTextureSize
|| minSize
.height
> maxTextureSize
) {
541 aOutMinSize
= minSize
;
543 DrawTarget
* destDT
= aDestinationCtx
->GetDrawTarget();
546 BlurCacheData
* cached
=
547 gBlurCache
->Lookup(minSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
548 destDT
->GetBackendType());
550 // See CreateBoxShadow() for these values
551 aOutBlurMargin
= cached
->mBlurMargin
;
552 RefPtr
<SourceSurface
> blur
= cached
->mBlur
;
553 return blur
.forget();
557 RefPtr
<SourceSurface
> boxShadow
=
558 CreateBoxShadow(destDT
, minSize
, aCornerRadii
, aBlurRadius
, aShadowColor
,
559 aMirrorCorners
, aOutBlurMargin
);
564 if (RefPtr
<SourceSurface
> opt
= destDT
->OptimizeSourceSurface(boxShadow
)) {
569 CacheBlur(destDT
, minSize
, aBlurRadius
, aCornerRadii
, aShadowColor
,
570 aOutBlurMargin
, boxShadow
);
572 return boxShadow
.forget();
575 void gfxAlphaBoxBlur::ShutdownBlurCache() {
577 gBlurCache
= nullptr;
580 static Rect
RectWithEdgesTRBL(Float aTop
, Float aRight
, Float aBottom
,
582 return Rect(aLeft
, aTop
, aRight
- aLeft
, aBottom
- aTop
);
585 static bool ShouldStretchSurface(DrawTarget
* aDT
, SourceSurface
* aSurface
) {
586 // Use stretching if possible, since it leads to less seams when the
587 // destination is transformed. However, don't do this if we're using cairo,
588 // because if cairo is using pixman it won't render anything for large
589 // stretch factors because pixman's internal fixed point precision is not
590 // high enough to handle those scale factors.
591 return aDT
->GetBackendType() != BackendType::CAIRO
;
594 static void RepeatOrStretchSurface(DrawTarget
* aDT
, SourceSurface
* aSurface
,
595 const Rect
& aDest
, const Rect
& aSrc
,
596 const Rect
& aSkipRect
) {
597 if (aSkipRect
.Contains(aDest
)) {
601 if (ShouldStretchSurface(aDT
, aSurface
)) {
602 aDT
->DrawSurface(aSurface
, aDest
, aSrc
);
606 SurfacePattern
pattern(aSurface
, ExtendMode::REPEAT
,
607 Matrix::Translation(aDest
.TopLeft() - aSrc
.TopLeft()),
608 SamplingFilter::GOOD
, RoundedToInt(aSrc
));
609 aDT
->FillRect(aDest
, pattern
);
612 static void DrawCorner(DrawTarget
* aDT
, SourceSurface
* aSurface
,
613 const Rect
& aDest
, const Rect
& aSrc
,
614 const Rect
& aSkipRect
) {
615 if (aSkipRect
.Contains(aDest
)) {
619 aDT
->DrawSurface(aSurface
, aDest
, aSrc
);
622 static void DrawMinBoxShadow(DrawTarget
* aDestDrawTarget
,
623 SourceSurface
* aSourceBlur
, const Rect
& aDstOuter
,
624 const Rect
& aDstInner
, const Rect
& aSrcOuter
,
625 const Rect
& aSrcInner
, const Rect
& aSkipRect
,
626 bool aMiddle
= false) {
627 // Corners: top left, top right, bottom left, bottom right
628 DrawCorner(aDestDrawTarget
, aSourceBlur
,
629 RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.X(), aDstInner
.Y(),
631 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.X(), aSrcInner
.Y(),
635 DrawCorner(aDestDrawTarget
, aSourceBlur
,
636 RectWithEdgesTRBL(aDstOuter
.Y(), aDstOuter
.XMost(), aDstInner
.Y(),
638 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcOuter
.XMost(), aSrcInner
.Y(),
642 DrawCorner(aDestDrawTarget
, aSourceBlur
,
643 RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.X(),
644 aDstOuter
.YMost(), aDstOuter
.X()),
645 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcInner
.X(),
646 aSrcOuter
.YMost(), aSrcOuter
.X()),
649 DrawCorner(aDestDrawTarget
, aSourceBlur
,
650 RectWithEdgesTRBL(aDstInner
.YMost(), aDstOuter
.XMost(),
651 aDstOuter
.YMost(), aDstInner
.XMost()),
652 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcOuter
.XMost(),
653 aSrcOuter
.YMost(), aSrcInner
.XMost()),
656 // Edges: top, left, right, bottom
657 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
658 RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.XMost(),
659 aDstInner
.Y(), aDstInner
.X()),
660 RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
661 aSrcInner
.Y(), aSrcInner
.X()),
663 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
664 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.X(),
665 aDstInner
.YMost(), aDstOuter
.X()),
666 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
667 aSrcInner
.YMost(), aSrcOuter
.X()),
670 RepeatOrStretchSurface(
671 aDestDrawTarget
, aSourceBlur
,
672 RectWithEdgesTRBL(aDstInner
.Y(), aDstOuter
.XMost(), aDstInner
.YMost(),
674 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcOuter
.XMost(), aSrcInner
.YMost(),
677 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
678 RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.XMost(),
679 aDstOuter
.YMost(), aDstInner
.X()),
680 RectWithEdgesTRBL(aSrcInner
.YMost(), aSrcInner
.XMost(),
681 aSrcOuter
.YMost(), aSrcInner
.X()),
686 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
687 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.XMost(),
688 aDstInner
.YMost(), aDstInner
.X()),
689 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.XMost(),
690 aSrcInner
.YMost(), aSrcInner
.X()),
695 static void DrawMirroredRect(DrawTarget
* aDT
, SourceSurface
* aSurface
,
696 const Rect
& aDest
, const Point
& aSrc
,
697 Float aScaleX
, Float aScaleY
) {
698 SurfacePattern
pattern(
699 aSurface
, ExtendMode::CLAMP
,
700 Matrix::Scaling(aScaleX
, aScaleY
)
702 .PostTranslate(aScaleX
< 0 ? aDest
.XMost() : aDest
.X(),
703 aScaleY
< 0 ? aDest
.YMost() : aDest
.Y()));
704 aDT
->FillRect(aDest
, pattern
);
707 static void DrawMirroredBoxShadow(DrawTarget
* aDT
, SourceSurface
* aSurface
,
708 const Rect
& aDestRect
) {
709 Point
center(ceil(aDestRect
.X() + aDestRect
.Width() / 2),
710 ceil(aDestRect
.Y() + aDestRect
.Height() / 2));
711 Rect
topLeft(aDestRect
.X(), aDestRect
.Y(), center
.x
- aDestRect
.X(),
712 center
.y
- aDestRect
.Y());
713 Rect
bottomRight(topLeft
.BottomRight(), aDestRect
.Size() - topLeft
.Size());
714 Rect
topRight(bottomRight
.X(), topLeft
.Y(), bottomRight
.Width(),
716 Rect
bottomLeft(topLeft
.X(), bottomRight
.Y(), topLeft
.Width(),
717 bottomRight
.Height());
718 DrawMirroredRect(aDT
, aSurface
, topLeft
, Point(), 1, 1);
719 DrawMirroredRect(aDT
, aSurface
, topRight
, Point(), -1, 1);
720 DrawMirroredRect(aDT
, aSurface
, bottomLeft
, Point(), 1, -1);
721 DrawMirroredRect(aDT
, aSurface
, bottomRight
, Point(), -1, -1);
724 static void DrawMirroredCorner(DrawTarget
* aDT
, SourceSurface
* aSurface
,
725 const Rect
& aDest
, const Point
& aSrc
,
726 const Rect
& aSkipRect
, Float aScaleX
,
728 if (aSkipRect
.Contains(aDest
)) {
732 DrawMirroredRect(aDT
, aSurface
, aDest
, aSrc
, aScaleX
, aScaleY
);
735 static void RepeatOrStretchMirroredSurface(DrawTarget
* aDT
,
736 SourceSurface
* aSurface
,
737 const Rect
& aDest
, const Rect
& aSrc
,
738 const Rect
& aSkipRect
, Float aScaleX
,
740 if (aSkipRect
.Contains(aDest
)) {
744 if (ShouldStretchSurface(aDT
, aSurface
)) {
745 aScaleX
*= aDest
.Width() / aSrc
.Width();
746 aScaleY
*= aDest
.Height() / aSrc
.Height();
747 DrawMirroredRect(aDT
, aSurface
, aDest
, aSrc
.TopLeft(), aScaleX
, aScaleY
);
751 SurfacePattern
pattern(
752 aSurface
, ExtendMode::REPEAT
,
753 Matrix::Scaling(aScaleX
, aScaleY
)
754 .PreTranslate(-aSrc
.TopLeft())
755 .PostTranslate(aScaleX
< 0 ? aDest
.XMost() : aDest
.X(),
756 aScaleY
< 0 ? aDest
.YMost() : aDest
.Y()),
757 SamplingFilter::GOOD
, RoundedToInt(aSrc
));
758 aDT
->FillRect(aDest
, pattern
);
761 static void DrawMirroredMinBoxShadow(
762 DrawTarget
* aDestDrawTarget
, SourceSurface
* aSourceBlur
,
763 const Rect
& aDstOuter
, const Rect
& aDstInner
, const Rect
& aSrcOuter
,
764 const Rect
& aSrcInner
, const Rect
& aSkipRect
, bool aMiddle
= false) {
765 // Corners: top left, top right, bottom left, bottom right
766 // Compute quadrant bounds and then clip them to corners along
767 // dimensions where we need to stretch from min size.
768 Point
center(ceil(aDstOuter
.X() + aDstOuter
.Width() / 2),
769 ceil(aDstOuter
.Y() + aDstOuter
.Height() / 2));
770 Rect
topLeft(aDstOuter
.X(), aDstOuter
.Y(), center
.x
- aDstOuter
.X(),
771 center
.y
- aDstOuter
.Y());
772 Rect
bottomRight(topLeft
.BottomRight(), aDstOuter
.Size() - topLeft
.Size());
773 Rect
topRight(bottomRight
.X(), topLeft
.Y(), bottomRight
.Width(),
775 Rect
bottomLeft(topLeft
.X(), bottomRight
.Y(), topLeft
.Width(),
776 bottomRight
.Height());
778 // Check if the middle part has been minimized along each dimension.
779 // If so, those will be strecthed/drawn separately and need to be clipped out.
780 if (aSrcInner
.Width() == 1) {
781 topLeft
.SetRightEdge(aDstInner
.X());
782 topRight
.SetLeftEdge(aDstInner
.XMost());
783 bottomLeft
.SetRightEdge(aDstInner
.X());
784 bottomRight
.SetLeftEdge(aDstInner
.XMost());
786 if (aSrcInner
.Height() == 1) {
787 topLeft
.SetBottomEdge(aDstInner
.Y());
788 topRight
.SetBottomEdge(aDstInner
.Y());
789 bottomLeft
.SetTopEdge(aDstInner
.YMost());
790 bottomRight
.SetTopEdge(aDstInner
.YMost());
793 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, topLeft
, aSrcOuter
.TopLeft(),
795 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, topRight
,
796 aSrcOuter
.TopLeft(), aSkipRect
, -1, 1);
797 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, bottomLeft
,
798 aSrcOuter
.TopLeft(), aSkipRect
, 1, -1);
799 DrawMirroredCorner(aDestDrawTarget
, aSourceBlur
, bottomRight
,
800 aSrcOuter
.TopLeft(), aSkipRect
, -1, -1);
802 // Edges: top, bottom, left, right
803 // Draw middle edges where they need to be stretched. The top and left
804 // sections that are part of the top-left quadrant will be mirrored to
805 // the bottom and right sections, respectively.
806 if (aSrcInner
.Width() == 1) {
807 Rect dstTop
= RectWithEdgesTRBL(aDstOuter
.Y(), aDstInner
.XMost(),
808 aDstInner
.Y(), aDstInner
.X());
809 Rect srcTop
= RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
810 aSrcInner
.Y(), aSrcInner
.X());
811 Rect dstBottom
= RectWithEdgesTRBL(aDstInner
.YMost(), aDstInner
.XMost(),
812 aDstOuter
.YMost(), aDstInner
.X());
813 Rect srcBottom
= RectWithEdgesTRBL(aSrcOuter
.Y(), aSrcInner
.XMost(),
814 aSrcInner
.Y(), aSrcInner
.X());
815 // If we only need to stretch along the X axis and we're drawing
816 // the middle section, just sample all the way to the center of the
817 // source on the Y axis to avoid extra draw calls.
818 if (aMiddle
&& aSrcInner
.Height() != 1) {
819 dstTop
.SetBottomEdge(center
.y
);
820 srcTop
.SetHeight(dstTop
.Height());
821 dstBottom
.SetTopEdge(dstTop
.YMost());
822 srcBottom
.SetHeight(dstBottom
.Height());
824 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstTop
, srcTop
,
826 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstBottom
,
827 srcBottom
, aSkipRect
, 1, -1);
830 if (aSrcInner
.Height() == 1) {
831 Rect dstLeft
= RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.X(),
832 aDstInner
.YMost(), aDstOuter
.X());
833 Rect srcLeft
= RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
834 aSrcInner
.YMost(), aSrcOuter
.X());
835 Rect dstRight
= RectWithEdgesTRBL(aDstInner
.Y(), aDstOuter
.XMost(),
836 aDstInner
.YMost(), aDstInner
.XMost());
837 Rect srcRight
= RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.X(),
838 aSrcInner
.YMost(), aSrcOuter
.X());
839 // Only stretching on Y axis, so sample source to the center of the X axis.
840 if (aMiddle
&& aSrcInner
.Width() != 1) {
841 dstLeft
.SetRightEdge(center
.x
);
842 srcLeft
.SetWidth(dstLeft
.Width());
843 dstRight
.SetLeftEdge(dstLeft
.XMost());
844 srcRight
.SetWidth(dstRight
.Width());
846 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstLeft
,
847 srcLeft
, aSkipRect
, 1, 1);
848 RepeatOrStretchMirroredSurface(aDestDrawTarget
, aSourceBlur
, dstRight
,
849 srcRight
, aSkipRect
, -1, 1);
852 // If we need to stretch along both dimensions, then the middle part
853 // must be drawn separately.
854 if (aMiddle
&& aSrcInner
.Width() == 1 && aSrcInner
.Height() == 1) {
855 RepeatOrStretchSurface(aDestDrawTarget
, aSourceBlur
,
856 RectWithEdgesTRBL(aDstInner
.Y(), aDstInner
.XMost(),
857 aDstInner
.YMost(), aDstInner
.X()),
858 RectWithEdgesTRBL(aSrcInner
.Y(), aSrcInner
.XMost(),
859 aSrcInner
.YMost(), aSrcInner
.X()),
865 * We draw a blurred a rectangle by only blurring a smaller rectangle and
866 * splitting the rectangle into 9 parts.
867 * First, a small minimum source rect is calculated and used to create a blur
868 * mask since the actual blurring itself is expensive. Next, we use the mask
869 * with the given shadow color to create a minimally-sized box shadow of the
870 * right color. Finally, we cut out the 9 parts from the box-shadow source and
871 * paint each part in the right place, stretching the non-corner parts to fill
872 * the space between the corners.
876 void gfxAlphaBoxBlur::BlurRectangle(gfxContext
* aDestinationCtx
,
877 const gfxRect
& aRect
,
878 const RectCornerRadii
* aCornerRadii
,
879 const gfxPoint
& aBlurStdDev
,
880 const Color
& aShadowColor
,
881 const gfxRect
& aDirtyRect
,
882 const gfxRect
& aSkipRect
) {
883 if (!RectIsInt32Safe(ToRect(aRect
))) {
887 IntSize blurRadius
= CalculateBlurRadius(aBlurStdDev
);
888 bool mirrorCorners
= !aCornerRadii
|| aCornerRadii
->AreRadiiSame();
890 IntRect rect
= RoundedToInt(ToRect(aRect
));
891 IntMargin blurMargin
;
894 RefPtr
<SourceSurface
> boxShadow
=
895 GetBlur(aDestinationCtx
, rect
.Size(), blurRadius
, aCornerRadii
,
896 aShadowColor
, mirrorCorners
, blurMargin
, slice
, minSize
);
901 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
902 destDrawTarget
->PushClipRect(ToRect(aDirtyRect
));
904 // Copy the right parts from boxShadow into destDrawTarget. The middle parts
905 // will be stretched, border-image style.
907 Rect
srcOuter(Point(blurMargin
.left
, blurMargin
.top
), Size(minSize
));
908 Rect
srcInner(srcOuter
);
909 srcOuter
.Inflate(Margin(blurMargin
));
910 srcInner
.Deflate(Margin(slice
));
914 dstOuter
.Inflate(Margin(blurMargin
));
915 dstInner
.Deflate(Margin(slice
));
917 Rect skipRect
= ToRect(aSkipRect
);
919 if (minSize
== rect
.Size()) {
920 // The target rect is smaller than the minimal size so just draw the surface
922 DrawMirroredBoxShadow(destDrawTarget
, boxShadow
, dstOuter
);
924 destDrawTarget
->DrawSurface(boxShadow
, dstOuter
, srcOuter
);
928 DrawMirroredMinBoxShadow(destDrawTarget
, boxShadow
, dstOuter
, dstInner
,
929 srcOuter
, srcInner
, skipRect
, true);
931 DrawMinBoxShadow(destDrawTarget
, boxShadow
, dstOuter
, dstInner
, srcOuter
,
932 srcInner
, skipRect
, true);
936 // A note about anti-aliasing and seems between adjacent parts:
937 // We don't explicitly disable anti-aliasing in the DrawSurface calls above,
938 // so if there's a transform on destDrawTarget that is not pixel-aligned,
939 // there will be seams between adjacent parts of the box-shadow. It's hard to
940 // avoid those without the use of an intermediate surface.
941 // You might think that we could avoid those by just turning off AA, but there
942 // is a problem with that: Box-shadow rendering needs to clip out the
943 // element's border box, and we'd like that clip to have anti-aliasing -
944 // especially if the element has rounded corners! So we can't do that unless
945 // we have a way to say "Please anti-alias the clip, but don't antialias the
946 // destination rect of the DrawSurface call".
948 destDrawTarget
->PopClip();
951 static already_AddRefed
<Path
> GetBoxShadowInsetPath(
952 DrawTarget
* aDrawTarget
, const Rect aOuterRect
, const Rect aInnerRect
,
953 const RectCornerRadii
* aInnerClipRadii
) {
955 * We create an inset path by having two rects.
957 * -----------------------
958 * | ________________ |
961 * | ------------------ |
962 * |_____________________|
964 * The outer rect and the inside rect. The path
965 * creates a frame around the content where we draw the inset shadow.
967 RefPtr
<PathBuilder
> builder
=
968 aDrawTarget
->CreatePathBuilder(FillRule::FILL_EVEN_ODD
);
969 AppendRectToPath(builder
, aOuterRect
, true);
971 if (aInnerClipRadii
) {
972 AppendRoundedRectToPath(builder
, aInnerRect
, *aInnerClipRadii
, false);
974 AppendRectToPath(builder
, aInnerRect
, false);
976 return builder
->Finish();
979 static void FillDestinationPath(
980 gfxContext
* aDestinationCtx
, const Rect
& aDestinationRect
,
981 const Rect
& aShadowClipRect
, const Color
& aShadowColor
,
982 const RectCornerRadii
* aInnerClipRadii
= nullptr) {
983 // When there is no blur radius, fill the path onto the destination
985 aDestinationCtx
->SetColor(aShadowColor
);
986 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
987 RefPtr
<Path
> shadowPath
= GetBoxShadowInsetPath(
988 destDrawTarget
, aDestinationRect
, aShadowClipRect
, aInnerClipRadii
);
990 aDestinationCtx
->SetPath(shadowPath
);
991 aDestinationCtx
->Fill();
994 static void CacheInsetBlur(const IntSize
& aMinOuterSize
,
995 const IntSize
& aMinInnerSize
,
996 const IntSize
& aBlurRadius
,
997 const RectCornerRadii
* aCornerRadii
,
998 const Color
& aShadowColor
, BackendType aBackendType
,
999 SourceSurface
* aBoxShadow
) {
1000 bool isInsetBlur
= true;
1001 BlurCacheKey
key(aMinOuterSize
, aMinInnerSize
, aBlurRadius
, aCornerRadii
,
1002 aShadowColor
, isInsetBlur
, aBackendType
);
1003 IntMargin
blurMargin(0, 0, 0, 0);
1004 BlurCacheData
* data
=
1005 new BlurCacheData(aBoxShadow
, blurMargin
, std::move(key
));
1006 if (!gBlurCache
->RegisterEntry(data
)) {
1011 already_AddRefed
<SourceSurface
> gfxAlphaBoxBlur::GetInsetBlur(
1012 const Rect
& aOuterRect
, const Rect
& aWhitespaceRect
, bool aIsDestRect
,
1013 const Color
& aShadowColor
, const IntSize
& aBlurRadius
,
1014 const RectCornerRadii
* aInnerClipRadii
, DrawTarget
* aDestDrawTarget
,
1015 bool aMirrorCorners
) {
1017 gBlurCache
= new BlurCache();
1020 IntSize outerSize
= IntSize::Truncate(aOuterRect
.Size());
1021 IntSize whitespaceSize
= IntSize::Truncate(aWhitespaceRect
.Size());
1023 BlurCacheData
* cached
= gBlurCache
->LookupInsetBoxShadow(
1024 outerSize
, whitespaceSize
, aBlurRadius
, aInnerClipRadii
, aShadowColor
,
1025 aDestDrawTarget
->GetBackendType());
1027 // So we don't forget the actual cached blur
1028 RefPtr
<SourceSurface
> cachedBlur
= cached
->mBlur
;
1029 return cachedBlur
.forget();
1033 // If we can do a min rect, the whitespace rect will be expanded in Init to
1035 Rect blurRect
= aIsDestRect
? aOuterRect
: aWhitespaceRect
;
1036 // If mirroring corners, we only need to draw the top-left quadrant.
1037 // Use ceil to preserve the remaining 1x1 middle area for minimized box
1039 if (aMirrorCorners
) {
1040 blurRect
.SizeTo(ceil(blurRect
.Width() * 0.5f
),
1041 ceil(blurRect
.Height() * 0.5f
));
1043 IntSize
zeroSpread(0, 0);
1044 RefPtr
<DrawTarget
> minDrawTarget
=
1045 InitDrawTarget(aDestDrawTarget
, blurRect
, zeroSpread
, aBlurRadius
);
1046 if (!minDrawTarget
) {
1050 // This is really annoying. When we create the AlphaBoxBlur, the DrawTarget
1051 // has a translation applied to it that is the topLeft point. This is actually
1052 // the rect we gave it plus the blur radius. The rects we give this for the
1053 // outer and whitespace rects are based at (0, 0). We could either translate
1054 // those rects when we don't have a destination rect or ignore the translation
1055 // when using the dest rect. The dest rects layout gives us expect this
1058 minDrawTarget
->SetTransform(Matrix());
1061 // Fill in the path between the inside white space / outer rects
1062 // NOT the inner frame
1063 RefPtr
<Path
> maskPath
= GetBoxShadowInsetPath(
1064 minDrawTarget
, aOuterRect
, aWhitespaceRect
, aInnerClipRadii
);
1066 ColorPattern
black(Color(0.f
, 0.f
, 0.f
, 1.f
));
1067 minDrawTarget
->Fill(maskPath
, black
);
1069 // Blur and fill in with the color we actually wanted
1070 RefPtr
<SourceSurface
> minInsetBlur
= DoBlur(&aShadowColor
);
1071 if (!minInsetBlur
) {
1075 if (RefPtr
<SourceSurface
> opt
=
1076 aDestDrawTarget
->OptimizeSourceSurface(minInsetBlur
)) {
1081 CacheInsetBlur(outerSize
, whitespaceSize
, aBlurRadius
, aInnerClipRadii
,
1082 aShadowColor
, aDestDrawTarget
->GetBackendType(),
1086 return minInsetBlur
.forget();
1090 * We create our minimal rect with 2 rects.
1091 * The first is the inside whitespace rect, that is "cut out"
1092 * from the box. This is (1). This must be the size
1093 * of the blur radius + corner radius so we can have a big enough
1096 * The second (2) is one blur radius surrounding the inner
1097 * frame of (1). This is the amount of blur space required
1098 * to get a proper blend.
1101 * W = one blur + corner radii - known as inner margin
1102 * ___________________________________
1105 * | (2) | (1) | (2) |
1110 * |________________________________|
1112 static void GetBlurMargins(const RectCornerRadii
* aInnerClipRadii
,
1113 const IntSize
& aBlurRadius
, Margin
& aOutBlurMargin
,
1114 Margin
& aOutInnerMargin
) {
1115 Size
cornerSize(0, 0);
1116 if (aInnerClipRadii
) {
1117 const RectCornerRadii
& corners
= *aInnerClipRadii
;
1118 NS_FOR_CSS_FULL_CORNERS(i
) {
1119 cornerSize
.width
= std::max(cornerSize
.width
, corners
[i
].width
);
1120 cornerSize
.height
= std::max(cornerSize
.height
, corners
[i
].height
);
1124 // Only the inside whitespace size cares about the border radius size.
1125 // Outer sizes only care about blur.
1126 IntSize margin
= IntSize::Ceil(cornerSize
) + aBlurRadius
;
1128 aOutInnerMargin
.SizeTo(margin
.height
, margin
.width
, margin
.height
,
1130 aOutBlurMargin
.SizeTo(aBlurRadius
.height
, aBlurRadius
.width
,
1131 aBlurRadius
.height
, aBlurRadius
.width
);
1134 static bool GetInsetBoxShadowRects(const Margin
& aBlurMargin
,
1135 const Margin
& aInnerMargin
,
1136 const Rect
& aShadowClipRect
,
1137 const Rect
& aDestinationRect
,
1138 Rect
& aOutWhitespaceRect
,
1139 Rect
& aOutOuterRect
) {
1140 // We always copy (2 * blur radius) + corner radius worth of data to the
1141 // destination rect This covers the blend of the path + the actual blur Need
1142 // +1 so that we copy the edges correctly as we'll copy over the min box
1143 // shadow corners then the +1 for the edges between Note, the (x,y)
1144 // coordinates are from the blur margin since the frame outside the whitespace
1145 // rect is 1 blur radius extra space.
1146 Rect
insideWhiteSpace(aBlurMargin
.left
, aBlurMargin
.top
,
1147 aInnerMargin
.LeftRight() + 1,
1148 aInnerMargin
.TopBottom() + 1);
1150 // If the inner white space rect is larger than the shadow clip rect
1151 // our approach does not work as we'll just copy one corner
1152 // and cover the destination. In those cases, fallback to the destination rect
1153 bool useDestRect
= (aShadowClipRect
.Width() <= aInnerMargin
.LeftRight()) ||
1154 (aShadowClipRect
.Height() <= aInnerMargin
.TopBottom());
1157 aOutWhitespaceRect
= aShadowClipRect
;
1158 aOutOuterRect
= aDestinationRect
;
1160 aOutWhitespaceRect
= insideWhiteSpace
;
1161 aOutOuterRect
= aOutWhitespaceRect
;
1162 aOutOuterRect
.Inflate(aBlurMargin
);
1168 void gfxAlphaBoxBlur::BlurInsetBox(
1169 gfxContext
* aDestinationCtx
, const Rect
& aDestinationRect
,
1170 const Rect
& aShadowClipRect
, const IntSize
& aBlurRadius
,
1171 const Color
& aShadowColor
, const RectCornerRadii
* aInnerClipRadii
,
1172 const Rect
& aSkipRect
, const Point
& aShadowOffset
) {
1173 if ((aBlurRadius
.width
== 0 && aBlurRadius
.height
== 0) ||
1174 aShadowClipRect
.IsEmpty()) {
1175 FillDestinationPath(aDestinationCtx
, aDestinationRect
, aShadowClipRect
,
1176 aShadowColor
, aInnerClipRadii
);
1180 DrawTarget
* destDrawTarget
= aDestinationCtx
->GetDrawTarget();
1184 GetBlurMargins(aInnerClipRadii
, aBlurRadius
, blurMargin
, innerMargin
);
1186 Rect whitespaceRect
;
1189 GetInsetBoxShadowRects(blurMargin
, innerMargin
, aShadowClipRect
,
1190 aDestinationRect
, whitespaceRect
, outerRect
);
1192 // Check that the inset margin between the outer and whitespace rects is
1193 // symmetric, and that all corner radii are the same, in which case the blur
1195 Margin checkMargin
= outerRect
- whitespaceRect
;
1196 bool mirrorCorners
= checkMargin
.left
== checkMargin
.right
&&
1197 checkMargin
.top
== checkMargin
.bottom
&&
1198 (!aInnerClipRadii
|| aInnerClipRadii
->AreRadiiSame());
1199 RefPtr
<SourceSurface
> minBlur
=
1200 GetInsetBlur(outerRect
, whitespaceRect
, useDestRect
, aShadowColor
,
1201 aBlurRadius
, aInnerClipRadii
, destDrawTarget
, mirrorCorners
);
1207 Rect destBlur
= aDestinationRect
;
1208 destBlur
.Inflate(blurMargin
);
1209 if (mirrorCorners
) {
1210 DrawMirroredBoxShadow(destDrawTarget
, minBlur
.get(), destBlur
);
1212 Rect
srcBlur(Point(0, 0), Size(minBlur
->GetSize()));
1213 MOZ_ASSERT(RoundedOut(srcBlur
).Size() == RoundedOut(destBlur
).Size());
1214 destDrawTarget
->DrawSurface(minBlur
, destBlur
, srcBlur
);
1217 Rect
srcOuter(outerRect
);
1218 Rect
srcInner(srcOuter
);
1219 srcInner
.Deflate(blurMargin
); // The outer color fill
1220 srcInner
.Deflate(innerMargin
); // The inner whitespace
1222 // The shadow clip rect already takes into account the spread radius
1223 Rect
outerFillRect(aShadowClipRect
);
1224 outerFillRect
.Inflate(blurMargin
);
1225 FillDestinationPath(aDestinationCtx
, aDestinationRect
, outerFillRect
,
1228 // Inflate once for the frame around the whitespace
1229 Rect
destRect(aShadowClipRect
);
1230 destRect
.Inflate(blurMargin
);
1232 // Deflate for the blurred in white space
1233 Rect
destInnerRect(aShadowClipRect
);
1234 destInnerRect
.Deflate(innerMargin
);
1236 if (mirrorCorners
) {
1237 DrawMirroredMinBoxShadow(destDrawTarget
, minBlur
, destRect
, destInnerRect
,
1238 srcOuter
, srcInner
, aSkipRect
);
1240 DrawMinBoxShadow(destDrawTarget
, minBlur
, destRect
, destInnerRect
,
1241 srcOuter
, srcInner
, aSkipRect
);