Bug 1429508 - Allow created mask surfaces to be clipped to the necessary size when replaying a recording. r=bas

Add a command CreateClippedDrawTarget to DrawTarget, which takes the
max required size and a transform between this draw target and the one
to be created. The created draw target may have its size clipped to
the size of this draw target, transformed to the new target's
space. This means that the new surface will be large enough so
that it is rendered to this draw target correctly, but not necessarily
any larger.

Usually this will just create a draw target of the requested size, for
simplicity. However, when replaying a recorded draw target we do clip
the size to the base draw target's size. This is done using a
DrawTargetTiled, so when applying the mask in PopLayer, we must take
the SourceSurface's offset in to account.

MozReview-Commit-ID: 89ONElphzLu

--HG--
extra : rebase_source : 7eebeb66a2686a7b6f4ade36f3004ebb06abc2fe
This commit is contained in:
Jamie Nicol 2018-02-05 17:59:42 +00:00
parent 4c2f14974a
commit 61cdff34c5
7 changed files with 127 additions and 9 deletions

View File

@ -1287,6 +1287,17 @@ public:
return CreateSimilarDrawTarget(aSize, aFormat);
}
/**
* Create a similar DrawTarget whose requested size may be clipped based
* on this DrawTarget's rect transformed to the new target's space.
*/
virtual RefPtr<DrawTarget> CreateClippedDrawTarget(const IntSize& aMaxSize,
const Matrix& aTransform,
SurfaceFormat aFormat) const
{
return CreateSimilarDrawTarget(aMaxSize, aFormat);
}
/**
* Create a similar draw target, but if the draw target is not backed by a
* raster backend (for example, it is capturing or recording), force it to

View File

@ -251,9 +251,6 @@ DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT,
, mFinalDT(aDT->mFinalDT)
, mSize(aSize)
{
mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this,
aSize,
aFormat));
mFormat = aFormat;
}
@ -568,9 +565,20 @@ already_AddRefed<DrawTarget>
DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
{
RefPtr<DrawTarget> similarDT = new DrawTargetRecording(this, aSize, aFormat);
mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(similarDT.get(),
aSize,
aFormat));
return similarDT.forget();
}
RefPtr<DrawTarget>
DrawTargetRecording::CreateClippedDrawTarget(const IntSize& aMaxSize, const Matrix& aTransform, SurfaceFormat aFormat) const
{
RefPtr<DrawTarget> similarDT = new DrawTargetRecording(this, aMaxSize, aFormat);
mRecorder->RecordEvent(RecordedCreateClippedDrawTarget(similarDT.get(), aMaxSize, aTransform, aFormat));
return similarDT;
}
already_AddRefed<PathBuilder>
DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const
{

View File

@ -270,6 +270,14 @@ public:
virtual already_AddRefed<DrawTarget>
CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const override;
/**
* Create a similar DrawTarget whose requested size may be clipped based
* on this DrawTarget's rect transformed to the new target's space.
*/
virtual RefPtr<DrawTarget> CreateClippedDrawTarget(const IntSize& aMaxSize,
const Matrix& aTransform,
SurfaceFormat aFormat) const override;
/*
* Create a path builder with the specified fillmode.
*

View File

@ -2162,6 +2162,8 @@ DrawTargetSkia::PopLayer()
paint.setColor(SK_ColorTRANSPARENT);
}
maskMat.postTranslate(layer.mMask->GetRect().X(), layer.mMask->GetRect().Y());
sk_sp<SkImage> alphaMask = ExtractAlphaForSurface(layer.mMask);
if (!alphaMask) {
gfxDebug() << *this << ": PopLayer() failed to extract alpha for mask";

View File

@ -252,6 +252,7 @@ public:
FILTERNODESETATTRIBUTE,
FILTERNODESETINPUT,
CREATESIMILARDRAWTARGET,
CREATECLIPPEDDRAWTARGET,
FONTDATA,
FONTDESC,
PUSHLAYER,

View File

@ -146,6 +146,38 @@ private:
MOZ_IMPLICIT RecordedCreateSimilarDrawTarget(S &aStream);
};
class RecordedCreateClippedDrawTarget : public RecordedEventDerived<RecordedCreateClippedDrawTarget> {
public:
RecordedCreateClippedDrawTarget(ReferencePtr aRefPtr, const IntSize& aMaxSize, const Matrix& aTransform, SurfaceFormat aFormat)
: RecordedEventDerived(CREATECLIPPEDDRAWTARGET)
, mRefPtr(aRefPtr)
, mMaxSize(aMaxSize)
, mTransform(aTransform)
, mFormat(aFormat)
{
}
virtual bool PlayEvent(Translator *aTranslator) const override;
template<class S>
void Record(S &aStream) const;
virtual void OutputSimpleEventInfo(std::stringstream &aStringStream) const override;
virtual std::string GetName() const override { return "CreateClippedDrawTarget"; }
virtual ReferencePtr GetObjectRef() const override { return mRefPtr; }
ReferencePtr mRefPtr;
IntSize mMaxSize;
Matrix mTransform;
SurfaceFormat mFormat;
private:
friend class RecordedEvent;
template<class S>
MOZ_IMPLICIT RecordedCreateClippedDrawTarget(S &aStream);
};
class RecordedFillRect : public RecordedDrawingEvent<RecordedFillRect> {
public:
RecordedFillRect(DrawTarget *aDT, const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions)
@ -1672,6 +1704,60 @@ RecordedCreateSimilarDrawTarget::OutputSimpleEventInfo(std::stringstream &aStrin
aStringStream << "[" << mRefPtr << "] CreateSimilarDrawTarget (Size: " << mSize.width << "x" << mSize.height << ")";
}
inline bool
RecordedCreateClippedDrawTarget::PlayEvent(Translator *aTranslator) const
{
const IntRect baseRect = aTranslator->GetReferenceDrawTarget()->GetRect();
const IntRect transformedRect = RoundedToInt(mTransform.Inverse().TransformBounds(IntRectToRect(baseRect)));
const IntRect intersection = IntRect(IntPoint(0, 0), mMaxSize).Intersect(transformedRect);
RefPtr<DrawTarget> newDT = aTranslator->GetReferenceDrawTarget()->CreateSimilarDrawTarget(intersection.Size(), SurfaceFormat::A8);
// It's overkill to use a TiledDrawTarget for a single tile
// but it was the easiest way to get the offset handling working
gfx::TileSet tileset;
gfx::Tile tile;
tile.mDrawTarget = newDT;
tile.mTileOrigin = gfx::IntPoint(intersection.X(), intersection.Y());
tileset.mTiles = &tile;
tileset.mTileCount = 1;
newDT = gfx::Factory::CreateTiledDrawTarget(tileset);
// If we couldn't create a DrawTarget this will probably cause us to crash
// with nullptr later in the playback, so return false to abort.
if (!newDT) {
return false;
}
aTranslator->AddDrawTarget(mRefPtr, newDT);
return true;
}
template<class S>
void
RecordedCreateClippedDrawTarget::Record(S &aStream) const
{
WriteElement(aStream, mRefPtr);
WriteElement(aStream, mMaxSize);
WriteElement(aStream, mTransform);
WriteElement(aStream, mFormat);
}
template<class S>
RecordedCreateClippedDrawTarget::RecordedCreateClippedDrawTarget(S &aStream)
: RecordedEventDerived(CREATECLIPPEDDRAWTARGET)
{
ReadElement(aStream, mRefPtr);
ReadElement(aStream, mMaxSize);
ReadElement(aStream, mTransform);
ReadElement(aStream, mFormat);
}
inline void
RecordedCreateClippedDrawTarget::OutputSimpleEventInfo(std::stringstream &aStringStream) const
{
aStringStream << "[" << mRefPtr << "] CreateClippedDrawTarget ()";
}
struct GenericPattern
{
GenericPattern(const PatternStorage &aStorage, Translator *aTranslator)
@ -3321,6 +3407,7 @@ RecordedFilterNodeSetInput::OutputSimpleEventInfo(std::stringstream &aStringStre
f(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute); \
f(FILTERNODESETINPUT, RecordedFilterNodeSetInput); \
f(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget); \
f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget); \
f(FONTDATA, RecordedFontData); \
f(FONTDESC, RecordedFontDescriptor); \
f(PUSHLAYER, RecordedPushLayer); \

View File

@ -795,17 +795,22 @@ GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
IntRect drawRect =
RoundedOut(ToRect(sourceCtx->GetClipExtents(gfxContext::eDeviceSpace)));
Matrix currentMatrix = sourceCtx->CurrentMatrix();
Matrix maskTransform = currentMatrix *
Matrix::Translation(-drawRect.x, -drawRect.y);
maskTransform.Invert();
// Create a mask surface.
RefPtr<DrawTarget> sourceTarget = sourceCtx->GetDrawTarget();
RefPtr<DrawTarget> maskDT =
sourceTarget->CreateSimilarDrawTarget(drawRect.Size(),
sourceTarget->CreateClippedDrawTarget(drawRect.Size(),
maskTransform * currentMatrix,
SurfaceFormat::A8);
if (!maskDT || !maskDT->IsValid()) {
return false;
}
RefPtr<gfxContext> maskCtx = gfxContext::CreatePreservingTransformOrNull(maskDT);
MOZ_ASSERT(maskCtx);
Matrix currentMatrix = sourceCtx->CurrentMatrix();
maskCtx->SetMatrix(Matrix::Translation(bounds.TopLeft().ToUnknownPoint()) *
currentMatrix *
Matrix::Translation(-drawRect.TopLeft()));
@ -818,10 +823,6 @@ GenerateAndPushTextMask(nsIFrame* aFrame, gfxContext* aContext,
// Push the generated mask into aContext, so that the caller can pop and
// blend with it.
Matrix maskTransform = currentMatrix *
Matrix::Translation(-drawRect.x, -drawRect.y);
maskTransform.Invert();
RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
sourceCtx->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, 1.0, maskSurface, maskTransform);