Bug 1429508 - Allow created mask surfaces to be clipped to the necessary size when replaying a recording. r?jrmuizel
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
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -1283,16 +1283,27 @@ public:
virtual already_AddRefed<DrawTarget>
CreateShadowDrawTarget(const IntSize &aSize, SurfaceFormat aFormat,
float aSigma) const
{
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
* create a raster target instead. This is intended for code that wants to
* cache pixels, and would have no effect if it were caching a recording.
*/
virtual RefPtr<DrawTarget>
CreateSimilarRasterTarget(const IntSize& aSize, SurfaceFormat aFormat) const
{
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -246,19 +246,16 @@ DrawTargetRecording::DrawTargetRecording
DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT,
IntSize aSize,
SurfaceFormat aFormat)
: mRecorder(aDT->mRecorder)
, mFinalDT(aDT->mFinalDT)
, mSize(aSize)
{
- mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this,
- aSize,
- aFormat));
mFormat = aFormat;
}
DrawTargetRecording::~DrawTargetRecording()
{
mRecorder->RecordEvent(RecordedDrawTargetDestruction(ReferencePtr(this)));
}
@@ -562,19 +559,30 @@ DrawTargetRecording::CreateSourceSurface
MOZ_ASSERT(false);
return nullptr;
}
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
{
RefPtr<PathBuilder> builder = mFinalDT->CreatePathBuilder(aFillRule);
return MakeAndAddRef<PathBuilderRecording>(builder, aFillRule);
}
already_AddRefed<GradientStops>
--- a/gfx/2d/DrawTargetRecording.h
+++ b/gfx/2d/DrawTargetRecording.h
@@ -265,16 +265,24 @@ public:
CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const override;
/*
* Create a DrawTarget whose snapshot is optimized for use with this DrawTarget.
*/
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.
*
* We need the fill mode up front because of Direct2D.
* ID2D1SimplifiedGeometrySink requires the fill mode
* to be set before calling BeginFigure().
*/
virtual already_AddRefed<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const override;
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -2157,16 +2157,18 @@ DrawTargetSkia::PopLayer()
layerMat.preTranslate(layerBounds.x(), layerBounds.y());
if (layerImage) {
paint.setShader(layerImage->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &layerMat));
} else {
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";
} else {
mCanvas->save();
// The layer may be smaller than the canvas size, so make sure drawing is
// clipped to within the bounds of the layer.
--- a/gfx/2d/RecordedEvent.h
+++ b/gfx/2d/RecordedEvent.h
@@ -247,16 +247,17 @@ public:
SCALEDFONTDESTRUCTION,
MASKSURFACE,
FILTERNODECREATION,
FILTERNODEDESTRUCTION,
DRAWFILTER,
FILTERNODESETATTRIBUTE,
FILTERNODESETINPUT,
CREATESIMILARDRAWTARGET,
+ CREATECLIPPEDDRAWTARGET,
FONTDATA,
FONTDESC,
PUSHLAYER,
POPLAYER,
UNSCALEDFONTCREATION,
UNSCALEDFONTDESTRUCTION,
};
static const uint32_t kTotalEventTypes = RecordedEvent::FILTERNODESETINPUT + 1;
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -141,16 +141,48 @@ public:
private:
friend class RecordedEvent;
template<class S>
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)
: RecordedDrawingEvent(FILLRECT, aDT), mRect(aRect), mOptions(aOptions)
{
StorePattern(mPattern, aPattern);
}
@@ -1667,16 +1699,70 @@ RecordedCreateSimilarDrawTarget::Recorde
}
inline void
RecordedCreateSimilarDrawTarget::OutputSimpleEventInfo(std::stringstream &aStringStream) const
{
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)
: mPattern(nullptr), mTranslator(aTranslator)
{
mStorage = const_cast<PatternStorage*>(&aStorage);
}
@@ -3316,16 +3402,17 @@ RecordedFilterNodeSetInput::OutputSimple
f(SNAPSHOT, RecordedSnapshot); \
f(SCALEDFONTCREATION, RecordedScaledFontCreation); \
f(SCALEDFONTCREATIONBYINDEX, RecordedScaledFontCreationByIndex); \
f(SCALEDFONTDESTRUCTION, RecordedScaledFontDestruction); \
f(MASKSURFACE, RecordedMaskSurface); \
f(FILTERNODESETATTRIBUTE, RecordedFilterNodeSetAttribute); \
f(FILTERNODESETINPUT, RecordedFilterNodeSetInput); \
f(CREATESIMILARDRAWTARGET, RecordedCreateSimilarDrawTarget); \
+ f(CREATECLIPPEDDRAWTARGET, RecordedCreateClippedDrawTarget); \
f(FONTDATA, RecordedFontData); \
f(FONTDESC, RecordedFontDescriptor); \
f(PUSHLAYER, RecordedPushLayer); \
f(POPLAYER, RecordedPopLayer); \
f(UNSCALEDFONTCREATION, RecordedUnscaledFontCreation); \
f(UNSCALEDFONTDESTRUCTION, RecordedUnscaledFontDestruction);
template<class S>
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -790,43 +790,44 @@ GenerateAndPushTextMask(nsIFrame* aFrame
NS_RGB(255, 255, 255),
nsDisplayListBuilderMode::PAINTING_SELECTION_BACKGROUND);
}
// Evaluate required surface size.
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()));
// Shade text shape into mask A8 surface.
nsLayoutUtils::PaintFrame(maskCtx, aFrame,
nsRect(nsPoint(0, 0), aFrame->GetSize()),
NS_RGB(255, 255, 255),
nsDisplayListBuilderMode::GENERATE_GLYPH);
// 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);
return true;
}
/* static */ void
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(Layer* aLayer,