Issue #2364 - use DrawTarget::DrawSurfaceWithShadow to render box shadows on platforms that accelerate it.

This commit is contained in:
Moonchild 2023-10-30 13:03:32 +01:00 committed by roytam1
commit 4beb3b947c
13 changed files with 228 additions and 150 deletions

View file

@ -20,7 +20,8 @@ using namespace mozilla;
using namespace mozilla::gfx;
gfxAlphaBoxBlur::gfxAlphaBoxBlur()
: mData(nullptr)
: mData(nullptr),
mAccelerated(false)
{
}
@ -32,16 +33,18 @@ gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
}
already_AddRefed<gfxContext>
gfxAlphaBoxBlur::Init(const gfxRect& aRect,
gfxAlphaBoxBlur::Init(gfxContext* aDestinationCtx,
const gfxRect& aRect,
const IntSize& aSpreadRadius,
const IntSize& aBlurRadius,
const gfxRect* aDirtyRect,
const gfxRect* aSkipRect)
{
DrawTarget* refDT = aDestinationCtx->GetDrawTarget();
Maybe<Rect> dirtyRect = aDirtyRect ? Some(ToRect(*aDirtyRect)) : Nothing();
Maybe<Rect> skipRect = aSkipRect ? Some(ToRect(*aSkipRect)) : Nothing();
RefPtr<DrawTarget> dt =
InitDrawTarget(ToRect(aRect), aSpreadRadius, aBlurRadius,
InitDrawTarget(refDT, ToRect(aRect), aSpreadRadius, aBlurRadius,
dirtyRect.ptrOr(nullptr), skipRect.ptrOr(nullptr));
if (!dt) {
return nullptr;
@ -54,7 +57,8 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
}
already_AddRefed<DrawTarget>
gfxAlphaBoxBlur::InitDrawTarget(const Rect& aRect,
gfxAlphaBoxBlur::InitDrawTarget(const DrawTarget* aReferenceDT,
const Rect& aRect,
const IntSize& aSpreadRadius,
const IntSize& aBlurRadius,
const Rect* aDirtyRect,
@ -66,90 +70,134 @@ gfxAlphaBoxBlur::InitDrawTarget(const Rect& aRect,
return nullptr;
}
// Make an alpha-only surface to draw on. We will play with the data after
// everything is drawn to create a blur effect.
mData = static_cast<uint8_t*>(calloc(1, blurDataSize));
if (!mData) {
return nullptr;
}
BackendType backend = aReferenceDT->GetBackendType();
RefPtr<DrawTarget> dt =
gfxPlatform::CreateDrawTargetForData(mData,
// Check if the backend has an accelerated DrawSurfaceWithShadow.
// Currently, only D2D1.1 supports this.
// Otherwise, DrawSurfaceWithShadow only supports square blurs without spread.
if (aBlurRadius.IsSquare() && aSpreadRadius.IsEmpty() &&
backend == BackendType::DIRECT2D1_1) {
mAccelerated = true;
mDrawTarget =
aReferenceDT->CreateShadowDrawTarget(mBlur.GetSize(),
SurfaceFormat::A8,
AlphaBoxBlur::CalculateBlurSigma(aBlurRadius.width));
} else {
// Make an alpha-only surface to draw on. We will play with the data after
// everything is drawn to create a blur effect.
mData = static_cast<uint8_t*>(calloc(1, blurDataSize));
if (!mData) {
return nullptr;
}
mDrawTarget =
Factory::DoesBackendSupportDataDrawtarget(backend) ?
Factory::CreateDrawTargetForData(backend,
mData,
mBlur.GetSize(),
mBlur.GetStride(),
SurfaceFormat::A8);
if (!dt || !dt->IsValid()) {
SurfaceFormat::A8) :
gfxPlatform::CreateDrawTargetForData(mData,
mBlur.GetSize(),
mBlur.GetStride(),
SurfaceFormat::A8);
}
if (!mDrawTarget || !mDrawTarget->IsValid()) {
return nullptr;
}
dt->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft()));
return dt.forget();
}
void
DrawBlur(gfxContext* aDestinationCtx,
SourceSurface* aBlur,
const IntPoint& aTopLeft,
const Rect* aDirtyRect)
{
DrawTarget *dest = aDestinationCtx->GetDrawTarget();
RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern();
Pattern* pat = thebesPat->GetPattern(dest, nullptr);
Matrix oldTransform = dest->GetTransform();
Matrix newTransform = oldTransform;
newTransform.PreTranslate(aTopLeft.x, aTopLeft.y);
// Avoid a semi-expensive clip operation if we can, otherwise
// clip to the dirty rect
if (aDirtyRect) {
dest->PushClipRect(*aDirtyRect);
}
dest->SetTransform(newTransform);
dest->MaskSurface(*pat, aBlur, Point(0, 0));
dest->SetTransform(oldTransform);
if (aDirtyRect) {
dest->PopClip();
}
mDrawTarget->SetTransform(Matrix::Translation(-mBlur.GetRect().TopLeft()));
return do_AddRef(mDrawTarget);
}
already_AddRefed<SourceSurface>
gfxAlphaBoxBlur::DoBlur(DrawTarget* aDT, IntPoint* aTopLeft)
gfxAlphaBoxBlur::DoBlur(const Color* aShadowColor, IntPoint* aOutTopLeft)
{
if (mData) {
mBlur.Blur(mData);
}
*aTopLeft = mBlur.GetRect().TopLeft();
if (aOutTopLeft) {
*aOutTopLeft = mBlur.GetRect().TopLeft();
}
return aDT->CreateSourceSurfaceFromData(mData,
mBlur.GetSize(),
mBlur.GetStride(),
SurfaceFormat::A8);
RefPtr<SourceSurface> blurMask = mDrawTarget->Snapshot();
if (mAccelerated) {
RefPtr<DrawTarget> blurDT =
Factory::CreateDrawTarget(mDrawTarget->GetBackendType(),
blurMask->GetSize(),
SurfaceFormat::A8);
if (!blurDT) {
return nullptr;
}
blurDT->DrawSurfaceWithShadow(blurMask, Point(0, 0), Color(1, 1, 1), Point(0, 0),
AlphaBoxBlur::CalculateBlurSigma(mBlur.GetBlurRadius().width),
CompositionOp::OP_OVER);
blurMask = blurDT->Snapshot();
}
if (!aShadowColor) {
return blurMask.forget();
}
RefPtr<DrawTarget> shadowDT =
Factory::CreateDrawTarget(mDrawTarget->GetBackendType(),
blurMask->GetSize(),
SurfaceFormat::B8G8R8A8);
if (!shadowDT) {
return nullptr;
}
ColorPattern shadowColor(ToDeviceColor(*aShadowColor));
shadowDT->MaskSurface(shadowColor, blurMask, Point(0, 0));
return shadowDT->Snapshot();
}
void
gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx)
{
if (!mData)
return;
if (!mAccelerated && !mData) {
return;
}
DrawTarget *dest = aDestinationCtx->GetDrawTarget();
if (!dest) {
NS_WARNING("Blurring not supported for Thebes contexts!");
return;
}
DrawTarget *dest = aDestinationCtx->GetDrawTarget();
if (!dest) {
NS_WARNING("Blurring not supported for Thebes contexts!");
return;
}
Rect* dirtyRect = mBlur.GetDirtyRect();
RefPtr<gfxPattern> thebesPat = aDestinationCtx->GetPattern();
Pattern* pat = thebesPat->GetPattern(dest, nullptr);
if (!pat) {
NS_WARNING("Failed to get pattern for blur!");
return;
}
IntPoint topLeft;
RefPtr<SourceSurface> mask = DoBlur(dest, &topLeft);
if (!mask) {
NS_ERROR("Failed to create mask!");
return;
}
IntPoint topLeft;
RefPtr<SourceSurface> mask = DoBlur(nullptr, &topLeft);
if (!mask) {
NS_ERROR("Failed to create mask!");
return;
}
DrawBlur(aDestinationCtx, mask, topLeft, dirtyRect);
// Avoid a semi-expensive clip operation if we can, otherwise
// clip to the dirty rect
Rect* dirtyRect = mBlur.GetDirtyRect();
if (dirtyRect) {
dest->PushClipRect(*dirtyRect);
}
Matrix oldTransform = dest->GetTransform();
Matrix newTransform = oldTransform;
newTransform.PreTranslate(topLeft);
dest->SetTransform(newTransform);
dest->MaskSurface(*pat, mask, Point(0, 0));
dest->SetTransform(oldTransform);
if (dirtyRect) {
dest->PopClip();
}
}
IntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd)
@ -433,14 +481,15 @@ CacheBlur(DrawTarget* aDT,
}
}
// Blurs a small surface and creates the mask.
// Blurs a small surface and creates the colored box shadow.
static already_AddRefed<SourceSurface>
CreateBlurMask(DrawTarget* aDestDrawTarget,
const IntSize& aMinSize,
const RectCornerRadii* aCornerRadii,
const IntSize& aBlurRadius,
bool aMirrorCorners,
IntMargin& aOutBlurMargin)
CreateBoxShadow(DrawTarget* aDestDrawTarget,
const IntSize& aMinSize,
const RectCornerRadii* aCornerRadii,
const IntSize& aBlurRadius,
const Color& aShadowColor,
bool aMirrorCorners,
IntMargin& aOutBlurMargin)
{
gfxAlphaBoxBlur blur;
Rect minRect(Point(0, 0), Size(aMinSize));
@ -453,7 +502,7 @@ CreateBlurMask(DrawTarget* aDestDrawTarget,
}
IntSize zeroSpread(0, 0);
RefPtr<DrawTarget> blurDT =
blur.InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
blur.InitDrawTarget(aDestDrawTarget, blurRect, zeroSpread, aBlurRadius);
if (!blurDT) {
return nullptr;
}
@ -469,7 +518,7 @@ CreateBlurMask(DrawTarget* aDestDrawTarget,
}
IntPoint topLeft;
RefPtr<SourceSurface> result = blur.DoBlur(aDestDrawTarget, &topLeft);
RefPtr<SourceSurface> result = blur.DoBlur(&aShadowColor, &topLeft);
if (!result) {
return nullptr;
}
@ -482,22 +531,6 @@ CreateBlurMask(DrawTarget* aDestDrawTarget,
return result.forget();
}
static already_AddRefed<SourceSurface>
CreateBoxShadow(DrawTarget* aDestDT, SourceSurface* aBlurMask, const Color& aShadowColor)
{
IntSize blurredSize = aBlurMask->GetSize();
RefPtr<DrawTarget> boxShadowDT =
Factory::CreateDrawTarget(aDestDT->GetBackendType(), blurredSize, SurfaceFormat::B8G8R8A8);
if (!boxShadowDT) {
return nullptr;
}
ColorPattern shadowColor(ToDeviceColor(aShadowColor));
boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
return boxShadowDT->Snapshot();
}
static already_AddRefed<SourceSurface>
GetBlur(gfxContext* aDestinationCtx,
const IntSize& aRectSize,
@ -533,21 +566,16 @@ GetBlur(gfxContext* aDestinationCtx,
aCornerRadii, aShadowColor,
destDT->GetBackendType());
if (cached) {
// See CreateBlurMask() for these values
// See CreateBoxShadow() for these values
aOutBlurMargin = cached->mBlurMargin;
RefPtr<SourceSurface> blur = cached->mBlur;
return blur.forget();
}
}
RefPtr<SourceSurface> blurMask =
CreateBlurMask(destDT, minSize, aCornerRadii, aBlurRadius,
aMirrorCorners, aOutBlurMargin);
if (!blurMask) {
return nullptr;
}
RefPtr<SourceSurface> boxShadow = CreateBoxShadow(destDT, blurMask, aShadowColor);
RefPtr<SourceSurface> boxShadow =
CreateBoxShadow(destDT, minSize, aCornerRadii, aBlurRadius,
aShadowColor, aMirrorCorners, aOutBlurMargin);
if (!boxShadow) {
return nullptr;
}
@ -1065,7 +1093,7 @@ gfxAlphaBoxBlur::GetInsetBlur(const Rect& aOuterRect,
}
IntSize zeroSpread(0, 0);
RefPtr<DrawTarget> minDrawTarget =
InitDrawTarget(blurRect, zeroSpread, aBlurRadius);
InitDrawTarget(aDestDrawTarget, blurRect, zeroSpread, aBlurRadius);
if (!minDrawTarget) {
return nullptr;
}
@ -1089,15 +1117,8 @@ gfxAlphaBoxBlur::GetInsetBlur(const Rect& aOuterRect,
ColorPattern black(Color(0.f, 0.f, 0.f, 1.f));
minDrawTarget->Fill(maskPath, black);
// Create the A8 mask
IntPoint topLeft;
RefPtr<SourceSurface> minMask = DoBlur(minDrawTarget, &topLeft);
if (!minMask) {
return nullptr;
}
// Fill in with the color we actually wanted
RefPtr<SourceSurface> minInsetBlur = CreateBoxShadow(aDestDrawTarget, minMask, aShadowColor);
// Blur and fill in with the color we actually wanted
RefPtr<SourceSurface> minInsetBlur = DoBlur(&aShadowColor);
if (!minInsetBlur) {
return nullptr;
}