mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 15:27:32 +09:00
Issue #2364 - use DrawTarget::DrawSurfaceWithShadow to render box shadows on platforms that accelerate it.
This commit is contained in:
parent
8d30faf95f
commit
4beb3b947c
13 changed files with 228 additions and 150 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue