зеркало из https://github.com/mozilla/gecko-dev.git
Bug 927892 - Split shadow drawing out of AdjustedTarget. r=Bas
--HG-- extra : rebase_source : cd02f322140f672e396c58330780ac085a97f103
This commit is contained in:
Родитель
c543b926f0
Коммит
6cc62b90f4
|
@ -86,6 +86,7 @@
|
||||||
#include "mozilla/MathAlgorithms.h"
|
#include "mozilla/MathAlgorithms.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "mozilla/unused.h"
|
#include "mozilla/unused.h"
|
||||||
#include "nsCCUncollectableMarker.h"
|
#include "nsCCUncollectableMarker.h"
|
||||||
#include "nsWrapperCacheInlines.h"
|
#include "nsWrapperCacheInlines.h"
|
||||||
|
@ -289,80 +290,57 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
/* This is an RAII based class that can be used as a drawtarget for
|
/* This is an RAII based class that can be used as a drawtarget for
|
||||||
* operations that need a shadow drawn. It will automatically provide a
|
* operations that need to have a shadow applied to their results.
|
||||||
* temporary target when needed, and if so blend it back with a shadow.
|
* All coordinates passed to the constructor are in device space.
|
||||||
*
|
|
||||||
* aBounds specifies the bounds of the drawing operation that will be
|
|
||||||
* drawn to the target, it is given in device space! This function will
|
|
||||||
* change aBounds to incorporate shadow bounds. If this is nullptr the drawing
|
|
||||||
* operation will be assumed to cover an infinite rect.
|
|
||||||
*/
|
*/
|
||||||
class AdjustedTarget
|
class AdjustedTargetForShadow
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
typedef CanvasRenderingContext2D::ContextState ContextState;
|
typedef CanvasRenderingContext2D::ContextState ContextState;
|
||||||
|
|
||||||
explicit AdjustedTarget(CanvasRenderingContext2D* ctx,
|
AdjustedTargetForShadow(CanvasRenderingContext2D *ctx,
|
||||||
mgfx::Rect *aBounds = nullptr)
|
DrawTarget *aFinalTarget,
|
||||||
|
const mgfx::Rect& aBounds,
|
||||||
|
mgfx::CompositionOp aCompositionOp)
|
||||||
: mCtx(nullptr)
|
: mCtx(nullptr)
|
||||||
|
, mCompositionOp(aCompositionOp)
|
||||||
{
|
{
|
||||||
if (!ctx->NeedToDrawShadow()) {
|
|
||||||
mTarget = ctx->mTarget;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mCtx = ctx;
|
mCtx = ctx;
|
||||||
|
mFinalTarget = aFinalTarget;
|
||||||
|
|
||||||
const ContextState &state = mCtx->CurrentState();
|
const ContextState &state = mCtx->CurrentState();
|
||||||
|
|
||||||
mSigma = state.shadowBlur / 2.0f;
|
mSigma = state.ShadowBlurSigma();
|
||||||
|
|
||||||
if (mSigma > SIGMA_MAX) {
|
mgfx::Rect bounds = aBounds;
|
||||||
mSigma = SIGMA_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
Matrix transform = mCtx->mTarget->GetTransform();
|
int32_t blurRadius = state.ShadowBlurRadius();
|
||||||
|
|
||||||
mTempRect = mgfx::Rect(0, 0, ctx->mWidth, ctx->mHeight);
|
|
||||||
|
|
||||||
static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
|
||||||
int32_t blurRadius = (int32_t) floor(mSigma * GAUSSIAN_SCALE_FACTOR + 0.5);
|
|
||||||
|
|
||||||
// We need to enlarge and possibly offset our temporary surface
|
|
||||||
// so that things outside of the canvas may cast shadows.
|
|
||||||
mTempRect.Inflate(Margin(blurRadius + std::max<Float>(state.shadowOffset.y, 0),
|
|
||||||
blurRadius + std::max<Float>(-state.shadowOffset.x, 0),
|
|
||||||
blurRadius + std::max<Float>(-state.shadowOffset.y, 0),
|
|
||||||
blurRadius + std::max<Float>(state.shadowOffset.x, 0)));
|
|
||||||
|
|
||||||
if (aBounds) {
|
|
||||||
// We actually include the bounds of the shadow blur, this makes it
|
// We actually include the bounds of the shadow blur, this makes it
|
||||||
// easier to execute the actual blur on hardware, and shouldn't affect
|
// easier to execute the actual blur on hardware, and shouldn't affect
|
||||||
// the amount of pixels that need to be touched.
|
// the amount of pixels that need to be touched.
|
||||||
aBounds->Inflate(Margin(blurRadius, blurRadius,
|
bounds.Inflate(blurRadius);
|
||||||
blurRadius, blurRadius));
|
|
||||||
mTempRect = mTempRect.Intersect(*aBounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
mTempRect.ScaleRoundOut(1.0f);
|
bounds.RoundOut();
|
||||||
|
bounds.ToIntRect(&mTempRect);
|
||||||
transform._31 -= mTempRect.x;
|
|
||||||
transform._32 -= mTempRect.y;
|
|
||||||
|
|
||||||
mTarget =
|
mTarget =
|
||||||
mCtx->mTarget->CreateShadowDrawTarget(IntSize(int32_t(mTempRect.width), int32_t(mTempRect.height)),
|
mFinalTarget->CreateShadowDrawTarget(mTempRect.Size(),
|
||||||
SurfaceFormat::B8G8R8A8, mSigma);
|
SurfaceFormat::B8G8R8A8, mSigma);
|
||||||
|
|
||||||
if (!mTarget) {
|
if (!mTarget) {
|
||||||
// XXX - Deal with the situation where our temp size is too big to
|
// XXX - Deal with the situation where our temp size is too big to
|
||||||
// fit in a texture.
|
// fit in a texture (bug 1066622).
|
||||||
mTarget = ctx->mTarget;
|
mTarget = mFinalTarget;
|
||||||
mCtx = nullptr;
|
mCtx = nullptr;
|
||||||
|
mFinalTarget = nullptr;
|
||||||
} else {
|
} else {
|
||||||
mTarget->SetTransform(transform);
|
mTarget->SetTransform(
|
||||||
|
mFinalTarget->GetTransform().PostTranslate(-mTempRect.TopLeft()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~AdjustedTarget()
|
~AdjustedTargetForShadow()
|
||||||
{
|
{
|
||||||
if (!mCtx) {
|
if (!mCtx) {
|
||||||
return;
|
return;
|
||||||
|
@ -370,10 +348,74 @@ public:
|
||||||
|
|
||||||
RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
|
RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
|
||||||
|
|
||||||
mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
|
mFinalTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
|
||||||
Color::FromABGR(mCtx->CurrentState().shadowColor),
|
Color::FromABGR(mCtx->CurrentState().shadowColor),
|
||||||
mCtx->CurrentState().shadowOffset, mSigma,
|
mCtx->CurrentState().shadowOffset, mSigma,
|
||||||
mCtx->CurrentState().op);
|
mCompositionOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawTarget* DT()
|
||||||
|
{
|
||||||
|
return mTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgfx::IntPoint OffsetToFinalDT()
|
||||||
|
{
|
||||||
|
return mTempRect.TopLeft();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<DrawTarget> mTarget;
|
||||||
|
RefPtr<DrawTarget> mFinalTarget;
|
||||||
|
CanvasRenderingContext2D *mCtx;
|
||||||
|
Float mSigma;
|
||||||
|
mgfx::IntRect mTempRect;
|
||||||
|
mgfx::CompositionOp mCompositionOp;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* This is an RAII based class that can be used as a drawtarget for
|
||||||
|
* operations that need a shadow or a filter drawn. It will automatically
|
||||||
|
* provide a temporary target when needed, and if so blend it back with a
|
||||||
|
* shadow, filter, or both.
|
||||||
|
* If both a shadow and a filter are needed, the filter is applied first,
|
||||||
|
* and the shadow is applied to the filtered results.
|
||||||
|
*
|
||||||
|
* aBounds specifies the bounds of the drawing operation that will be
|
||||||
|
* drawn to the target, it is given in device space! If this is nullptr the
|
||||||
|
* drawing operation will be assumed to cover the whole canvas.
|
||||||
|
*/
|
||||||
|
class AdjustedTarget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef CanvasRenderingContext2D::ContextState ContextState;
|
||||||
|
|
||||||
|
explicit AdjustedTarget(CanvasRenderingContext2D* ctx,
|
||||||
|
const mgfx::Rect *aBounds = nullptr)
|
||||||
|
{
|
||||||
|
mTarget = ctx->mTarget;
|
||||||
|
|
||||||
|
// All rects in this function are in the device space of ctx->mTarget.
|
||||||
|
|
||||||
|
// In order to keep our temporary surfaces as small as possible, we first
|
||||||
|
// calculate what their maximum required bounds would need to be if we
|
||||||
|
// were to fill the whole canvas. Everything outside those bounds we don't
|
||||||
|
// need to render.
|
||||||
|
mgfx::Rect r(0, 0, ctx->mWidth, ctx->mHeight);
|
||||||
|
mgfx::Rect maxSourceNeededBoundsForShadow =
|
||||||
|
MaxSourceNeededBoundsForShadow(r, ctx);
|
||||||
|
|
||||||
|
mgfx::Rect bounds = maxSourceNeededBoundsForShadow;
|
||||||
|
if (aBounds) {
|
||||||
|
bounds = bounds.Intersect(*aBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::gfx::CompositionOp op = ctx->CurrentState().op;
|
||||||
|
|
||||||
|
if (ctx->NeedToDrawShadow()) {
|
||||||
|
mShadowTarget = MakeUnique<AdjustedTargetForShadow>(
|
||||||
|
ctx, mTarget, bounds, op);
|
||||||
|
mTarget = mShadowTarget->DT();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
operator DrawTarget*()
|
operator DrawTarget*()
|
||||||
|
@ -387,10 +429,25 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
mgfx::Rect
|
||||||
|
MaxSourceNeededBoundsForShadow(const mgfx::Rect& aDestBounds, CanvasRenderingContext2D *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx->NeedToDrawShadow()) {
|
||||||
|
return aDestBounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContextState &state = ctx->CurrentState();
|
||||||
|
mgfx::Rect sourceBounds = aDestBounds - state.shadowOffset;
|
||||||
|
sourceBounds.Inflate(state.ShadowBlurRadius());
|
||||||
|
|
||||||
|
// Union the shadow source with the original rect because we're going to
|
||||||
|
// draw both.
|
||||||
|
return sourceBounds.Union(aDestBounds);
|
||||||
|
}
|
||||||
|
|
||||||
RefPtr<DrawTarget> mTarget;
|
RefPtr<DrawTarget> mTarget;
|
||||||
CanvasRenderingContext2D *mCtx;
|
UniquePtr<AdjustedTargetForShadow> mShadowTarget;
|
||||||
Float mSigma;
|
|
||||||
mgfx::Rect mTempRect;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -991,6 +991,16 @@ protected:
|
||||||
return !(patternStyles[whichStyle] || gradientStyles[whichStyle]);
|
return !(patternStyles[whichStyle] || gradientStyles[whichStyle]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t ShadowBlurRadius() const
|
||||||
|
{
|
||||||
|
static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||||
|
return (int32_t)floor(ShadowBlurSigma() * GAUSSIAN_SCALE_FACTOR + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::gfx::Float ShadowBlurSigma() const
|
||||||
|
{
|
||||||
|
return std::min(SIGMA_MAX, shadowBlur / 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<mozilla::RefPtr<mozilla::gfx::Path> > clipsPushed;
|
std::vector<mozilla::RefPtr<mozilla::gfx::Path> > clipsPushed;
|
||||||
|
|
||||||
|
@ -1044,6 +1054,7 @@ protected:
|
||||||
friend class CanvasGeneralPattern;
|
friend class CanvasGeneralPattern;
|
||||||
friend class CanvasFilterChainObserver;
|
friend class CanvasFilterChainObserver;
|
||||||
friend class AdjustedTarget;
|
friend class AdjustedTarget;
|
||||||
|
friend class AdjustedTargetForShadow;
|
||||||
|
|
||||||
// other helpers
|
// other helpers
|
||||||
void GetAppUnitsValues(int32_t *perDevPixel, int32_t *perCSSPixel)
|
void GetAppUnitsValues(int32_t *perDevPixel, int32_t *perCSSPixel)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче