Bug 1210560 - Part 5: Convert BasicLayers usecases to PushGroupForBlendBack and temporary surfaces. r=jrmuizel
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1252,16 +1252,17 @@ DrawTargetCairo::Mask(const Pattern &aSo
if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
cairo_pattern_destroy(source);
cairo_pattern_destroy(mask);
gfxWarning() << "Invalid pattern";
return;
}
cairo_set_source(mContext, source);
+ cairo_set_operator(mContext, GfxOpToCairoOp(aOptions.mCompositionOp));
cairo_mask(mContext, mask);
cairo_pattern_destroy(mask);
cairo_pattern_destroy(source);
}
void
DrawTargetCairo::MaskSurface(const Pattern &aSource,
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -76,46 +76,123 @@ ClipToContain(gfxContext* aContext, cons
aContext->NewPath();
aContext->Rectangle(deviceRect);
aContext->Clip();
aContext->SetMatrix(currentMatrix);
return aContext->DeviceToUser(deviceRect).IsEqualInterior(userRect);
}
-already_AddRefed<gfxContext>
-BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
- const nsIntRegion& aRegion,
- bool* aNeedsClipToVisibleRegion)
+BasicLayerManager::PushedGroup
+BasicLayerManager::PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion)
{
+ PushedGroup group;
+
+ group.mVisibleRegion = aRegion;
+ group.mFinalTarget = aContext;
+ group.mOperator = GetEffectiveOperator(aLayer);
+ group.mOpacity = aLayer->GetEffectiveOpacity();
+
// If we need to call PushGroup, we should clip to the smallest possible
// area first to minimize the size of the temporary surface.
bool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
- RefPtr<gfxContext> result;
+ bool canPushGroup = group.mOperator == CompositionOp::OP_OVER ||
+ (group.mOperator == CompositionOp::OP_SOURCE && (aLayer->CanUseOpaqueSurface() || aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA));
+
+ if (!canPushGroup) {
+ aContext->Save();
+ gfxUtils::ClipToRegion(group.mFinalTarget, group.mVisibleRegion);
+
+ // PushGroup/PopGroup do not support non operator over.
+ gfxMatrix oldMat = aContext->CurrentMatrix();
+ aContext->SetMatrix(gfxMatrix());
+ gfxRect rect = aContext->GetClipExtents();
+ aContext->SetMatrix(oldMat);
+ rect.RoundOut();
+ IntRect surfRect;
+ ToRect(rect).ToIntRect(&surfRect);
+
+ RefPtr<DrawTarget> dt = aContext->GetDrawTarget()->CreateSimilarDrawTarget(surfRect.Size(), SurfaceFormat::B8G8R8A8);
+
+ RefPtr<gfxContext> ctx = new gfxContext(dt, ToRect(rect).TopLeft());
+ ctx->SetMatrix(oldMat);
+
+ group.mGroupOffset = surfRect.TopLeft();
+ group.mGroupTarget = ctx;
+
+ group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
+ return group;
+ }
+
+ Matrix maskTransform;
+ RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
+
if (aLayer->CanUseOpaqueSurface() &&
((didCompleteClip && aRegion.GetNumRects() == 1) ||
!aContext->CurrentMatrix().HasNonIntegerTranslation())) {
// If the layer is opaque in its visible region we can push a gfxContentType::COLOR
// group. We need to make sure that only pixels inside the layer's visible
// region are copied back to the destination. Remember if we've already
// clipped precisely to the visible region.
- *aNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
- aContext->PushGroup(gfxContentType::COLOR);
- result = aContext;
+ group.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Save();
+ gfxUtils::ClipToRegion(group.mFinalTarget, group.mVisibleRegion);
+ }
+
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR, group.mOpacity, maskSurf, maskTransform);
} else {
- *aNeedsClipToVisibleRegion = false;
- result = aContext;
if (aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) {
- aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA);
+ aContext->PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA, group.mOpacity, maskSurf, maskTransform);
} else {
- aContext->PushGroup(gfxContentType::COLOR_ALPHA);
+ aContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, group.mOpacity, maskSurf, maskTransform);
}
}
- return result.forget();
+
+ group.mGroupTarget = group.mFinalTarget;
+ return group;
+}
+
+void
+BasicLayerManager::PopGroupForLayer(PushedGroup &group)
+{
+ if (group.mFinalTarget == group.mGroupTarget) {
+ group.mFinalTarget->PopGroupAndBlend();
+ if (group.mNeedsClipToVisibleRegion) {
+ group.mFinalTarget->Restore();
+ }
+ return;
+ }
+
+ DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
+ RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
+ group.mGroupTarget = nullptr;
+
+ RefPtr<SourceSurface> src = sourceDT->Snapshot();
+
+ if (group.mMaskSurface) {
+ dt->SetTransform(group.mMaskTransform * Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+ group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
+ } else {
+ // For now this is required since our group offset is in device space of the final target,
+ // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
+ // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
+ // always become null.
+ dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
+ dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
+ Rect(0, 0, src->GetSize().width, src->GetSize().height), DrawSurfaceOptions(Filter::POINT), DrawOptions(group.mOpacity, group.mOperator));
+ }
+
+ if (group.mNeedsClipToVisibleRegion) {
+ dt->PopClip();
+ }
+
+ group.mFinalTarget->Restore();
}
static IntRect
ToInsideIntRect(const gfxRect& aRect)
{
gfxRect r = aRect;
r.RoundIn();
return IntRect(r.X(), r.Y(), r.Width(), r.Height());
@@ -968,21 +1045,20 @@ BasicLayerManager::PaintLayer(gfxContext
bool clipIsEmpty = aTarget->GetClipExtents().IsEmpty();
if (clipIsEmpty) {
PaintSelfOrChildren(paintLayerContext, aTarget);
return;
}
if (is2D) {
if (needsGroup) {
- RefPtr<gfxContext> groupTarget = PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion(),
- &needsClipToVisibleRegion);
- PaintSelfOrChildren(paintLayerContext, groupTarget);
- aTarget->PopGroupToSource();
- FlushGroup(paintLayerContext, needsClipToVisibleRegion);
+ PushedGroup pushedGroup =
+ PushGroupForLayer(aTarget, aLayer, aLayer->GetEffectiveVisibleRegion());
+ PaintSelfOrChildren(paintLayerContext, pushedGroup.mGroupTarget);
+ PopGroupForLayer(pushedGroup);
} else {
PaintSelfOrChildren(paintLayerContext, aTarget);
}
} else {
if (!needsGroup && container) {
PaintSelfOrChildren(paintLayerContext, aTarget);
return;
}
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -133,19 +133,33 @@ public:
virtual const char* Name() const override { return "Basic"; }
// Clear the cached contents of this layer tree.
virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
void SetTransactionIncomplete() { mTransactionIncomplete = true; }
bool IsTransactionIncomplete() { return mTransactionIncomplete; }
- already_AddRefed<gfxContext> PushGroupForLayer(gfxContext* aContext, Layer* aLayer,
- const nsIntRegion& aRegion,
- bool* aNeedsClipToVisibleRegion);
+ struct PushedGroup
+ {
+ PushedGroup() : mFinalTarget(nullptr), mNeedsClipToVisibleRegion(false) {}
+ gfxContext* mFinalTarget;
+ RefPtr<gfxContext> mGroupTarget;
+ nsIntRegion mVisibleRegion;
+ bool mNeedsClipToVisibleRegion;
+ gfx::IntPoint mGroupOffset;
+ gfx::CompositionOp mOperator;
+ gfx::Float mOpacity;
+ RefPtr<gfx::SourceSurface> mMaskSurface;
+ gfx::Matrix mMaskTransform;
+ };
+
+ PushedGroup PushGroupForLayer(gfxContext* aContext, Layer* aLayerContext, const nsIntRegion& aRegion);
+
+ void PopGroupForLayer(PushedGroup& aGroup);
virtual bool IsCompositingCheap() override { return false; }
virtual int32_t GetMaxTextureSize() const override { return INT32_MAX; }
bool CompositorMightResample() { return mCompositorMightResample; }
virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) override { return true; }
protected:
--- a/gfx/layers/basic/BasicLayersImpl.cpp
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -34,16 +34,35 @@ GetMaskData(Layer* aMaskLayer,
transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
aMaskData->Construct(transform, surface);
return true;
}
}
return false;
}
+already_AddRefed<SourceSurface>
+GetMaskForLayer(Layer* aLayer, Matrix* aMaskTransform)
+{
+ if (!aLayer->GetMaskLayer()) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aMaskTransform);
+
+ AutoMoz2DMaskData mask;
+ if (GetMaskData(aLayer->GetMaskLayer(), Point(), &mask)) {
+ *aMaskTransform = mask.GetTransform();
+ RefPtr<SourceSurface> surf = mask.GetSurface();
+ return surf.forget();
+ }
+
+ return nullptr;
+}
+
void
PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
{
AutoMoz2DMaskData mask;
if (GetMaskData(aMaskLayer, Point(), &mask)) {
aContext->SetMatrix(ThebesMatrix(mask.GetTransform()));
aContext->Mask(mask.GetSurface(), aOpacity);
return;
--- a/gfx/layers/basic/BasicLayersImpl.h
+++ b/gfx/layers/basic/BasicLayersImpl.h
@@ -83,16 +83,18 @@ protected:
* false otherwise.
* The transform for the layer will be put in aMaskData
*/
bool
GetMaskData(Layer* aMaskLayer,
const gfx::Point& aDeviceOffset,
AutoMoz2DMaskData* aMaskData);
+already_AddRefed<gfx::SourceSurface> GetMaskForLayer(Layer* aLayer, gfx::Matrix* aMaskTransform);
+
// Paint the current source to a context using a mask, if present
void
PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
// Fill the rect with the source, using a mask and opacity, if present
void
FillRectWithMask(gfx::DrawTarget* aDT,
const gfx::Rect& aRect,
--- a/gfx/layers/basic/BasicPaintedLayer.cpp
+++ b/gfx/layers/basic/BasicPaintedLayer.cpp
@@ -70,41 +70,33 @@ BasicPaintedLayer::PaintThebes(gfxContex
if (!toDraw.IsEmpty() && !IsHidden()) {
if (!aCallback) {
BasicManager()->SetTransactionIncomplete();
return;
}
aContext->Save();
- bool needsClipToVisibleRegion = GetClipToVisibleRegion();
bool needsGroup = opacity != 1.0 ||
effectiveOperator != CompositionOp::OP_OVER ||
aMaskLayer;
RefPtr<gfxContext> groupContext;
+ BasicLayerManager::PushedGroup group;
if (needsGroup) {
- groupContext =
- BasicManager()->PushGroupForLayer(aContext, this, toDraw,
- &needsClipToVisibleRegion);
- if (effectiveOperator != CompositionOp::OP_OVER) {
- needsClipToVisibleRegion = true;
- }
+ group =
+ BasicManager()->PushGroupForLayer(aContext, this, toDraw);
+ groupContext = group.mGroupTarget;
} else {
groupContext = aContext;
}
SetAntialiasingFlags(this, groupContext->GetDrawTarget());
aCallback(this, groupContext, toDraw, toDraw,
DrawRegionClip::NONE, nsIntRegion(), aCallbackData);
if (needsGroup) {
- aContext->PopGroupToSource();
- if (needsClipToVisibleRegion) {
- gfxUtils::ClipToRegion(aContext, toDraw);
- }
- AutoSetOperator setOptimizedOperator(aContext, effectiveOperator);
- PaintWithMask(aContext, opacity, aMaskLayer);
+ BasicManager()->PopGroupForLayer(group);
}
aContext->Restore();
}
RenderTraceInvalidateEnd(this, "FFFF00");
return;
}
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -897,17 +897,17 @@ GetRoundOutDeviceClipExtents(gfxContext*
gfxContextMatrixAutoSaveRestore save(aCtx);
aCtx->SetMatrix(gfxMatrix());
gfxRect r = aCtx->GetClipExtents();
r.RoundOut();
return r;
}
void
-gfxContext::PushGroupAndCopyBackground(gfxContentType content)
+gfxContext::PushGroupAndCopyBackground(gfxContentType content, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform)
{
IntRect clipExtents;
if (mDT->GetFormat() != SurfaceFormat::B8G8R8X8) {
gfxRect clipRect = GetRoundOutDeviceClipExtents(this);
clipExtents = IntRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
}
if ((mDT->GetFormat() == SurfaceFormat::B8G8R8X8 ||
mDT->GetOpaqueRect().Contains(clipExtents)) &&
@@ -918,16 +918,21 @@ gfxContext::PushGroupAndCopyBackground(g
PushNewDT(gfxContentType::COLOR);
if (oldDT == mDT) {
// Creating new DT failed.
return;
}
+ CurrentState().mBlendOpacity = aOpacity;
+ CurrentState().mBlendMask = aMask;
+ CurrentState().mWasPushedForBlendBack = true;
+ CurrentState().mBlendMaskTransform = aMaskTransform;
+
Point offset = CurrentState().deviceOffset - oldDeviceOffset;
Rect surfRect(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
Rect sourceRect = surfRect + offset;
mDT->SetTransform(Matrix());
// XXX: It's really sad that we have to do this (for performance).
// Once DrawTarget gets a PushLayer API we can implement this within
@@ -953,16 +958,20 @@ gfxContext::PushGroupAndCopyBackground(g
}
mDT->SetOpaqueRect(oldDT->GetOpaqueRect());
PushClipsToDT(mDT);
mDT->SetTransform(GetDTTransform());
return;
}
PushGroup(content);
+ CurrentState().mBlendOpacity = aOpacity;
+ CurrentState().mBlendMask = aMask;
+ CurrentState().mWasPushedForBlendBack = true;
+ CurrentState().mBlendMaskTransform = aMaskTransform;
}
already_AddRefed<gfxPattern>
gfxContext::PopGroup()
{
RefPtr<SourceSurface> src = mDT->Snapshot();
Point deviceOffset = CurrentState().deviceOffset;
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -432,25 +432,28 @@ public:
*/
void PushGroup(gfxContentType content = gfxContentType::COLOR);
void PushGroupForBlendBack(gfxContentType content, mozilla::gfx::Float aOpacity = 1.0f,
mozilla::gfx::SourceSurface* aMask = nullptr,
const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
/**
- * Like PushGroup, but if the current surface is gfxContentType::COLOR and
+ * Like PushGroupForBlendBack, but if the current surface is gfxContentType::COLOR and
* content is gfxContentType::COLOR_ALPHA, makes the pushed surface gfxContentType::COLOR
* instead and copies the contents of the current surface to the pushed
* surface. This is good for pushing opacity groups, since blending the
* group back to the current surface with some alpha applied will give
* the correct results and using an opaque pushed surface gives better
* quality and performance.
*/
- void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR);
+ void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR,
+ mozilla::gfx::Float aOpacity = 1.0f,
+ mozilla::gfx::SourceSurface* aMask = nullptr,
+ const mozilla::gfx::Matrix& aMaskTransform = mozilla::gfx::Matrix());
already_AddRefed<gfxPattern> PopGroup();
void PopGroupToSource();
void PopGroupAndBlend();
already_AddRefed<mozilla::gfx::SourceSurface>
PopGroupToSurface(mozilla::gfx::Matrix* aMatrix);
mozilla::gfx::Point GetDeviceOffset() const;