gecko-dev/gfx/layers/basic/BasicLayers.cpp
Robert O'Callahan 173828d394 Bug 622072. Part 1: Remove rect parameter from Updated() and change implementations to defer updates to render time. r=bas
The rectangle parameter is currently not used --- all callers always pass the full canvas bounds. In the long term,
we probably won't want this parameter since all implementations should be doing accelerated drawing direct to buffers
with no intermediate copies, hence there will be no need to optimize the size of those copies. Plus, performance-sensitive
testcases tend to paint most or all of the canvas on every frame anyway.
2011-03-28 12:59:46 +13:00

2885 lines
90 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Corporation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Robert O'Callahan <robert@ocallahan.org>
* Chris Jones <jones.chris.g@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifdef MOZ_IPC
# include "gfxSharedImageSurface.h"
# include "mozilla/layers/PLayerChild.h"
# include "mozilla/layers/PLayersChild.h"
# include "mozilla/layers/PLayersParent.h"
# include "ipc/ShadowLayerChild.h"
#endif
#include "BasicLayers.h"
#include "ImageLayers.h"
#include "nsTArray.h"
#include "nsGUIEvent.h"
#include "nsIRenderingContext.h"
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxPattern.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "ThebesLayerBuffer.h"
#include "nsIWidget.h"
#include "ReadbackProcessor.h"
#include "GLContext.h"
namespace mozilla {
namespace layers {
class BasicContainerLayer;
class ShadowableLayer;
/**
* This is the ImplData for all Basic layers. It also exposes methods
* private to the Basic implementation that are common to all Basic layer types.
* In particular, there is an internal Paint() method that we can use
* to paint the contents of non-Thebes layers.
*
* The class hierarchy for Basic layers is like this:
* BasicImplData
* Layer | | |
* | | | |
* +-> ContainerLayer | | |
* | | | | |
* | +-> BasicContainerLayer <--+ | |
* | | |
* +-> ThebesLayer | |
* | | | |
* | +-> BasicThebesLayer <---------+ |
* | |
* +-> ImageLayer |
* | |
* +-> BasicImageLayer <--------------+
*/
class BasicImplData {
public:
BasicImplData()
{
MOZ_COUNT_CTOR(BasicImplData);
}
virtual ~BasicImplData()
{
MOZ_COUNT_DTOR(BasicImplData);
}
/**
* Layers that paint themselves, such as ImageLayers, should paint
* in response to this method call. aContext will already have been
* set up to account for all the properties of the layer (transform,
* opacity, etc).
*/
virtual void Paint(gfxContext* aContext) {}
/**
* Like Paint() but called for ThebesLayers with the additional parameters
* they need.
*/
virtual void PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback) {}
virtual ShadowableLayer* AsShadowableLayer() { return nsnull; }
/**
* Implementations return true here if they *must* retain their
* layer contents. This is true of shadowable layers with shadows,
* because there's no target on which to composite directly in the
* layer-publishing child process.
*/
virtual bool MustRetainContent() { return false; }
/**
* Layers will get this call when their layer manager is destroyed, this
* indicates they should clear resources they don't really need after their
* LayerManager ceases to exist.
*/
virtual void ClearCachedResources() {}
/**
* This variable is used by layer manager in order to
* MarkLeafLayersCoveredByOpaque() before painting.
* We keep it here for now. Once we need to cull completely covered
* non-Basic layers, mCoveredByOpaque should be moved to Layer.
*/
void SetCoveredByOpaque(PRBool aCovered) { mCoveredByOpaque = aCovered; }
PRBool IsCoveredByOpaque() const { return mCoveredByOpaque; }
protected:
PRPackedBool mCoveredByOpaque;
};
static BasicImplData*
ToData(Layer* aLayer)
{
return static_cast<BasicImplData*>(aLayer->ImplData());
}
template<class Container>
static void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
template<class Container>
static void ContainerRemoveChild(Layer* aChild, Container* aContainer);
class BasicContainerLayer : public ContainerLayer, BasicImplData {
template<class Container>
friend void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
template<class Container>
friend void ContainerRemoveChild(Layer* aChild, Container* aContainer);
public:
BasicContainerLayer(BasicLayerManager* aManager) :
ContainerLayer(aManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicContainerLayer);
mSupportsComponentAlphaChildren = PR_TRUE;
}
virtual ~BasicContainerLayer();
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ContainerLayer::SetVisibleRegion(aRegion);
}
virtual void InsertAfter(Layer* aChild, Layer* aAfter)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ContainerInsertAfter(aChild, aAfter, this);
}
virtual void RemoveChild(Layer* aChild)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ContainerRemoveChild(aChild, this);
}
virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
{
// We push groups for container layers if we need to, which always
// are aligned in device space, so it doesn't really matter how we snap
// containers.
gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull);
// We always pass the ideal matrix down to our children, so there is no
// need to apply any compensation using the residual from SnapTransform.
ComputeEffectiveTransformsForChildren(idealTransform);
/* If we have a single child, it can just inherit our opacity,
* otherwise we need a PushGroup and we need to mark ourselves as using
* an intermediate surface so our children don't inherit our opacity
* via GetEffectiveOpacity.
*/
mUseIntermediateSurface = GetEffectiveOpacity() != 1.0 && HasMultipleChildren();
}
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
};
BasicContainerLayer::~BasicContainerLayer()
{
while (mFirstChild) {
ContainerRemoveChild(mFirstChild, this);
}
MOZ_COUNT_DTOR(BasicContainerLayer);
}
template<class Container>
static void
ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer)
{
NS_ASSERTION(aChild->Manager() == aContainer->Manager(),
"Child has wrong manager");
NS_ASSERTION(!aChild->GetParent(),
"aChild already in the tree");
NS_ASSERTION(!aChild->GetNextSibling() && !aChild->GetPrevSibling(),
"aChild already has siblings?");
NS_ASSERTION(!aAfter ||
(aAfter->Manager() == aContainer->Manager() &&
aAfter->GetParent() == aContainer),
"aAfter is not our child");
aChild->SetParent(aContainer);
if (aAfter == aContainer->mLastChild) {
aContainer->mLastChild = aChild;
}
if (!aAfter) {
aChild->SetNextSibling(aContainer->mFirstChild);
if (aContainer->mFirstChild) {
aContainer->mFirstChild->SetPrevSibling(aChild);
}
aContainer->mFirstChild = aChild;
NS_ADDREF(aChild);
aContainer->DidInsertChild(aChild);
return;
}
Layer* next = aAfter->GetNextSibling();
aChild->SetNextSibling(next);
aChild->SetPrevSibling(aAfter);
if (next) {
next->SetPrevSibling(aChild);
}
aAfter->SetNextSibling(aChild);
NS_ADDREF(aChild);
aContainer->DidInsertChild(aChild);
}
template<class Container>
static void
ContainerRemoveChild(Layer* aChild, Container* aContainer)
{
NS_ASSERTION(aChild->Manager() == aContainer->Manager(),
"Child has wrong manager");
NS_ASSERTION(aChild->GetParent() == aContainer,
"aChild not our child");
Layer* prev = aChild->GetPrevSibling();
Layer* next = aChild->GetNextSibling();
if (prev) {
prev->SetNextSibling(next);
} else {
aContainer->mFirstChild = next;
}
if (next) {
next->SetPrevSibling(prev);
} else {
aContainer->mLastChild = prev;
}
aChild->SetNextSibling(nsnull);
aChild->SetPrevSibling(nsnull);
aChild->SetParent(nsnull);
aContainer->DidRemoveChild(aChild);
NS_RELEASE(aChild);
}
class BasicThebesLayer;
class BasicThebesLayerBuffer : public ThebesLayerBuffer {
typedef ThebesLayerBuffer Base;
public:
BasicThebesLayerBuffer(BasicThebesLayer* aLayer)
: Base(ContainsVisibleBounds)
, mLayer(aLayer)
{
}
virtual ~BasicThebesLayerBuffer()
{}
using Base::BufferRect;
using Base::BufferRotation;
/**
* Complete the drawing operation. The region to draw must have been
* drawn before this is called. The contents of the buffer are drawn
* to aTarget.
*/
void DrawTo(ThebesLayer* aLayer, gfxContext* aTarget, float aOpacity);
virtual already_AddRefed<gfxASurface>
CreateBuffer(ContentType aType, const nsIntSize& aSize);
/**
* Swap out the old backing buffer for |aBuffer| and attributes.
*/
void SetBackingBuffer(gfxASurface* aBuffer,
const nsIntRect& aRect, const nsIntPoint& aRotation)
{
gfxIntSize prevSize = gfxIntSize(BufferDims().width, BufferDims().height);
gfxIntSize newSize = aBuffer->GetSize();
NS_ABORT_IF_FALSE(newSize == prevSize,
"Swapped-in buffer size doesn't match old buffer's!");
nsRefPtr<gfxASurface> oldBuffer;
oldBuffer = SetBuffer(aBuffer, nsIntSize(newSize.width, newSize.height),
aRect, aRotation);
}
void SetBackingBufferAndUpdateFrom(
gfxASurface* aBuffer,
gfxASurface* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
const nsIntRegion& aUpdateRegion, float aXResolution, float aYResolution);
private:
BasicThebesLayerBuffer(gfxASurface* aBuffer,
const nsIntRect& aRect, const nsIntPoint& aRotation)
// The size policy doesn't really matter here; this constructor is
// intended to be used for creating temporaries
: ThebesLayerBuffer(ContainsVisibleBounds)
{
gfxIntSize sz = aBuffer->GetSize();
SetBuffer(aBuffer, nsIntSize(sz.width, sz.height), aRect, aRotation);
}
BasicThebesLayer* mLayer;
};
class BasicThebesLayer : public ThebesLayer, BasicImplData {
public:
typedef BasicThebesLayerBuffer Buffer;
BasicThebesLayer(BasicLayerManager* aLayerManager) :
ThebesLayer(aLayerManager, static_cast<BasicImplData*>(this)),
mBuffer(this)
{
MOZ_COUNT_CTOR(BasicThebesLayer);
}
virtual ~BasicThebesLayer()
{
MOZ_COUNT_DTOR(BasicThebesLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ThebesLayer::SetVisibleRegion(aRegion);
}
virtual void InvalidateRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
mValidRegion.Sub(mValidRegion, aRegion);
}
virtual void PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback);
virtual void ClearCachedResources() { mBuffer.Clear(); mValidRegion.SetEmpty(); }
virtual already_AddRefed<gfxASurface>
CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize)
{
nsRefPtr<gfxASurface> referenceSurface = mBuffer.GetBuffer();
if (!referenceSurface) {
gfxContext* defaultTarget = BasicManager()->GetDefaultTarget();
if (defaultTarget) {
referenceSurface = defaultTarget->CurrentSurface();
} else {
nsIWidget* widget = BasicManager()->GetRetainerWidget();
if (widget) {
referenceSurface = widget->GetThebesSurface();
} else {
referenceSurface = BasicManager()->GetTarget()->CurrentSurface();
}
}
}
return referenceSurface->CreateSimilarSurface(
aType, gfxIntSize(aSize.width, aSize.height));
}
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
virtual void
PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
PRBool aDidSelfCopy,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
if (!aCallback) {
BasicManager()->SetTransactionIncomplete();
return;
}
aCallback(this, aContext, aExtendedRegionToDraw, aRegionToInvalidate,
aCallbackData);
// Everything that's visible has been validated. Do this instead of just
// OR-ing with aRegionToDraw, since that can lead to a very complex region
// here (OR doesn't automatically simplify to the simplest possible
// representation of a region.)
nsIntRegion tmp;
tmp.Or(mVisibleRegion, aExtendedRegionToDraw);
mValidRegion.Or(mValidRegion, tmp);
}
Buffer mBuffer;
};
/**
* Clips to the smallest device-pixel-aligned rectangle containing aRect
* in user space.
* Returns true if the clip is "perfect", i.e. we actually clipped exactly to
* aRect.
*/
static PRBool
ClipToContain(gfxContext* aContext, const nsIntRect& aRect)
{
gfxRect userRect(aRect.x, aRect.y, aRect.width, aRect.height);
gfxRect deviceRect = aContext->UserToDevice(userRect);
deviceRect.RoundOut();
gfxMatrix currentMatrix = aContext->CurrentMatrix();
aContext->IdentityMatrix();
aContext->NewPath();
aContext->Rectangle(deviceRect);
aContext->Clip();
aContext->SetMatrix(currentMatrix);
return aContext->DeviceToUser(deviceRect) == userRect;
}
static nsIntRegion
IntersectWithClip(const nsIntRegion& aRegion, gfxContext* aContext)
{
gfxRect clip = aContext->GetClipExtents();
clip.RoundOut();
nsIntRect r(clip.X(), clip.Y(), clip.Width(), clip.Height());
nsIntRegion result;
result.And(aRegion, r);
return result;
}
static void
SetAntialiasingFlags(Layer* aLayer, gfxContext* aTarget)
{
nsRefPtr<gfxASurface> surface = aTarget->CurrentSurface();
if (surface->GetContentType() != gfxASurface::CONTENT_COLOR_ALPHA) {
// Destination doesn't have alpha channel; no need to set any special flags
return;
}
const nsIntRect& bounds = aLayer->GetVisibleRegion().GetBounds();
surface->SetSubpixelAntialiasingEnabled(
!(aLayer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA) ||
surface->GetOpaqueRect().Contains(
aTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height))));
}
static PRBool
PushGroupForLayer(gfxContext* aContext, Layer* aLayer, const nsIntRegion& aRegion)
{
// If we need to call PushGroup, we should clip to the smallest possible
// area first to minimize the size of the temporary surface.
PRBool didCompleteClip = ClipToContain(aContext, aRegion.GetBounds());
gfxASurface::gfxContentType contentType = gfxASurface::CONTENT_COLOR_ALPHA;
PRBool needsClipToVisibleRegion = PR_FALSE;
if (aLayer->CanUseOpaqueSurface() &&
((didCompleteClip && aRegion.GetNumRects() == 1) ||
!aContext->CurrentMatrix().HasNonIntegerTranslation())) {
// If the layer is opaque in its visible region we can push a CONTENT_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.
needsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
contentType = gfxASurface::CONTENT_COLOR;
}
aContext->PushGroupAndCopyBackground(contentType);
return needsClipToVisibleRegion;
}
void
BasicThebesLayer::PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback)
{
NS_ASSERTION(BasicManager()->InDrawing(),
"Can only draw in drawing phase");
nsRefPtr<gfxASurface> targetSurface = aContext->CurrentSurface();
nsTArray<ReadbackProcessor::Update> readbackUpdates;
if (aReadback && UsedForReadback()) {
aReadback->GetThebesLayerUpdates(this, &readbackUpdates);
}
PRBool canUseOpaqueSurface = CanUseOpaqueSurface();
Buffer::ContentType contentType =
canUseOpaqueSurface ? gfxASurface::CONTENT_COLOR :
gfxASurface::CONTENT_COLOR_ALPHA;
float opacity = GetEffectiveOpacity();
if (!BasicManager()->IsRetained() ||
(!canUseOpaqueSurface &&
(mContentFlags & CONTENT_COMPONENT_ALPHA) &&
!MustRetainContent())) {
NS_ASSERTION(readbackUpdates.IsEmpty(), "Can't do readback for non-retained layer");
mValidRegion.SetEmpty();
mBuffer.Clear();
nsIntRegion toDraw = IntersectWithClip(mVisibleRegion, aContext);
if (!toDraw.IsEmpty()) {
if (!aCallback) {
BasicManager()->SetTransactionIncomplete();
return;
}
aContext->Save();
PRBool needsClipToVisibleRegion = PR_FALSE;
if (opacity != 1.0) {
needsClipToVisibleRegion = PushGroupForLayer(aContext, this, toDraw);
}
SetAntialiasingFlags(this, aContext);
aCallback(this, aContext, toDraw, nsIntRegion(), aCallbackData);
if (opacity != 1.0) {
aContext->PopGroupToSource();
if (needsClipToVisibleRegion) {
gfxUtils::ClipToRegion(aContext, toDraw);
}
aContext->Paint(opacity);
}
aContext->Restore();
}
return;
}
{
gfxSize scale = aContext->CurrentMatrix().ScaleFactors(PR_TRUE);
float paintXRes = BasicManager()->XResolution() * gfxUtils::ClampToScaleFactor(scale.width);
float paintYRes = BasicManager()->YResolution() * gfxUtils::ClampToScaleFactor(scale.height);
PRUint32 flags = 0;
gfxMatrix transform;
if (!GetEffectiveTransform().Is2D(&transform) ||
transform.HasNonIntegerTranslation() ||
MustRetainContent() /*<=> has shadow layer*/) {
flags |= ThebesLayerBuffer::PAINT_WILL_RESAMPLE;
}
Buffer::PaintState state =
mBuffer.BeginPaint(this, contentType, paintXRes, paintYRes, flags);
mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
if (state.mContext) {
// The area that became invalid and is visible needs to be repainted
// (this could be the whole visible area if our buffer switched
// from RGB to RGBA, because we might need to repaint with
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
nsIntRegion extendedDrawRegion = state.mRegionToDraw;
extendedDrawRegion.ExtendForScaling(paintXRes, paintYRes);
mXResolution = paintXRes;
mYResolution = paintYRes;
SetAntialiasingFlags(this, state.mContext);
PaintBuffer(state.mContext,
state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate,
state.mDidSelfCopy,
aCallback, aCallbackData);
Mutated();
} else {
// It's possible that state.mRegionToInvalidate is nonempty here,
// if we are shrinking the valid region to nothing.
NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
"If we need to draw, we should have a context");
}
}
mBuffer.DrawTo(this, aContext, opacity);
for (PRUint32 i = 0; i < readbackUpdates.Length(); ++i) {
ReadbackProcessor::Update& update = readbackUpdates[i];
nsIntPoint offset = update.mLayer->GetBackgroundLayerOffset();
nsRefPtr<gfxContext> ctx =
update.mLayer->GetSink()->BeginUpdate(update.mUpdateRect + offset,
update.mSequenceCounter);
if (ctx) {
NS_ASSERTION(opacity == 1.0, "Should only read back opaque layers");
ctx->Translate(gfxPoint(offset.x, offset.y));
mBuffer.DrawTo(this, ctx, 1.0);
update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset);
}
}
}
static PRBool
IsClippingCheap(gfxContext* aTarget, const nsIntRegion& aRegion)
{
// Assume clipping is cheap if the context just has an integer
// translation, and the visible region is simple.
return !aTarget->CurrentMatrix().HasNonIntegerTranslation() &&
aRegion.GetNumRects() <= 1;
}
void
BasicThebesLayerBuffer::DrawTo(ThebesLayer* aLayer,
gfxContext* aTarget,
float aOpacity)
{
aTarget->Save();
// If the entire buffer is valid, we can just draw the whole thing,
// no need to clip. But we'll still clip if clipping is cheap ---
// that might let us copy a smaller region of the buffer.
if (!aLayer->GetValidRegion().Contains(BufferRect()) ||
IsClippingCheap(aTarget, aLayer->GetVisibleRegion())) {
// We don't want to draw invalid stuff, so we need to clip. Might as
// well clip to the smallest area possible --- the visible region.
// Bug 599189 if there is a non-integer-translation transform in aTarget,
// we might sample pixels outside GetVisibleRegion(), which is wrong
// and may cause gray lines.
gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetVisibleRegion());
}
DrawBufferWithRotation(aTarget, aOpacity,
aLayer->GetXResolution(), aLayer->GetYResolution());
aTarget->Restore();
}
already_AddRefed<gfxASurface>
BasicThebesLayerBuffer::CreateBuffer(ContentType aType,
const nsIntSize& aSize)
{
return mLayer->CreateBuffer(aType, aSize);
}
void
BasicThebesLayerBuffer::SetBackingBufferAndUpdateFrom(
gfxASurface* aBuffer,
gfxASurface* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
const nsIntRegion& aUpdateRegion, float aXResolution, float aYResolution)
{
SetBackingBuffer(aBuffer, aRect, aRotation);
nsRefPtr<gfxContext> destCtx =
GetContextForQuadrantUpdate(aUpdateRegion.GetBounds(),
aXResolution, aYResolution);
destCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
if (IsClippingCheap(destCtx, aUpdateRegion)) {
gfxUtils::ClipToRegion(destCtx, aUpdateRegion);
}
BasicThebesLayerBuffer srcBuffer(aSource, aRect, aRotation);
srcBuffer.DrawBufferWithRotation(destCtx, 1.0, aXResolution, aYResolution);
}
class BasicImageLayer : public ImageLayer, BasicImplData {
public:
BasicImageLayer(BasicLayerManager* aLayerManager) :
ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
mSize(-1, -1)
{
MOZ_COUNT_CTOR(BasicImageLayer);
}
virtual ~BasicImageLayer()
{
MOZ_COUNT_DTOR(BasicImageLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ImageLayer::SetVisibleRegion(aRegion);
}
virtual void Paint(gfxContext* aContext);
static void PaintContext(gfxPattern* aPattern,
const nsIntRegion& aVisible,
const nsIntRect* aTileSourceRect,
float aOpacity,
gfxContext* aContext);
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
already_AddRefed<gfxPattern>
GetAndPaintCurrentImage(gfxContext* aContext,
float aOpacity);
gfxIntSize mSize;
};
void
BasicImageLayer::Paint(gfxContext* aContext)
{
nsRefPtr<gfxPattern> dontcare =
GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
}
already_AddRefed<gfxPattern>
BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext,
float aOpacity)
{
if (!mContainer)
return nsnull;
nsRefPtr<Image> image = mContainer->GetCurrentImage();
nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&mSize);
if (!surface) {
return nsnull;
}
nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
if (!pat) {
return nsnull;
}
pat->SetFilter(mFilter);
// The visible region can extend outside the image. If we're not
// tiling, we don't want to draw into that area, so just draw within
// the image bounds.
const nsIntRect* tileSrcRect = GetTileSourceRect();
PaintContext(pat,
tileSrcRect ? GetVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
tileSrcRect,
aOpacity, aContext);
GetContainer()->NotifyPaintedImage(image);
return pat.forget();
}
/*static*/ void
BasicImageLayer::PaintContext(gfxPattern* aPattern,
const nsIntRegion& aVisible,
const nsIntRect* aTileSourceRect,
float aOpacity,
gfxContext* aContext)
{
// Set PAD mode so that when the video is being scaled, we do not sample
// outside the bounds of the video image.
gfxPattern::GraphicsExtend extend = gfxPattern::EXTEND_PAD;
// PAD is slow with X11 and Quartz surfaces, so prefer speed over correctness
// and use NONE.
nsRefPtr<gfxASurface> target = aContext->CurrentSurface();
gfxASurface::gfxSurfaceType type = target->GetType();
if (type == gfxASurface::SurfaceTypeXlib ||
type == gfxASurface::SurfaceTypeXcb ||
type == gfxASurface::SurfaceTypeQuartz) {
extend = gfxPattern::EXTEND_NONE;
}
if (!aTileSourceRect) {
aContext->NewPath();
// No need to snap here; our transform has already taken care of it.
// XXX true for arbitrary regions? Don't care yet though
gfxUtils::PathFromRegion(aContext, aVisible);
aPattern->SetExtend(extend);
aContext->SetPattern(aPattern);
aContext->FillWithOpacity(aOpacity);
} else {
nsRefPtr<gfxASurface> source = aPattern->GetSurface();
NS_ABORT_IF_FALSE(source, "Expecting a surface pattern");
gfxIntSize sourceSize = source->GetSize();
nsIntRect sourceRect(0, 0, sourceSize.width, sourceSize.height);
NS_ABORT_IF_FALSE(sourceRect == *aTileSourceRect,
"Cowardly refusing to create a temporary surface for tiling");
gfxContextAutoSaveRestore saveRestore(aContext);
aContext->NewPath();
gfxUtils::PathFromRegion(aContext, aVisible);
aPattern->SetExtend(gfxPattern::EXTEND_REPEAT);
aContext->SetPattern(aPattern);
aContext->FillWithOpacity(aOpacity);
}
// Reset extend mode for callers that need to reuse the pattern
aPattern->SetExtend(extend);
}
class BasicColorLayer : public ColorLayer, BasicImplData {
public:
BasicColorLayer(BasicLayerManager* aLayerManager) :
ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicColorLayer);
}
virtual ~BasicColorLayer()
{
MOZ_COUNT_DTOR(BasicColorLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ColorLayer::SetVisibleRegion(aRegion);
}
virtual void Paint(gfxContext* aContext)
{
PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
}
static void PaintColorTo(gfxRGBA aColor, float aOpacity,
gfxContext* aContext);
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
};
/*static*/ void
BasicColorLayer::PaintColorTo(gfxRGBA aColor, float aOpacity,
gfxContext* aContext)
{
aContext->SetColor(aColor);
aContext->Paint(aOpacity);
}
class BasicCanvasLayer : public CanvasLayer,
BasicImplData
{
public:
BasicCanvasLayer(BasicLayerManager* aLayerManager) :
CanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicCanvasLayer);
}
virtual ~BasicCanvasLayer()
{
MOZ_COUNT_DTOR(BasicCanvasLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
CanvasLayer::SetVisibleRegion(aRegion);
}
virtual void Initialize(const Data& aData);
virtual void Paint(gfxContext* aContext);
virtual void PaintWithOpacity(gfxContext* aContext,
float aOpacity);
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
void UpdateSurface();
nsRefPtr<gfxASurface> mSurface;
nsRefPtr<mozilla::gl::GLContext> mGLContext;
PRUint32 mCanvasFramebuffer;
PRPackedBool mGLBufferIsPremultiplied;
PRPackedBool mNeedsYFlip;
};
void
BasicCanvasLayer::Initialize(const Data& aData)
{
NS_ASSERTION(mSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
if (aData.mSurface) {
mSurface = aData.mSurface;
NS_ASSERTION(aData.mGLContext == nsnull,
"CanvasLayer can't have both surface and GLContext");
mNeedsYFlip = PR_FALSE;
} else if (aData.mGLContext) {
NS_ASSERTION(aData.mGLContext->IsOffscreen(), "canvas gl context isn't offscreen");
mGLContext = aData.mGLContext;
mGLBufferIsPremultiplied = aData.mGLBufferIsPremultiplied;
mCanvasFramebuffer = mGLContext->GetOffscreenFBO();
mNeedsYFlip = PR_TRUE;
} else {
NS_ERROR("CanvasLayer created without mSurface or mGLContext?");
}
mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
}
void
BasicCanvasLayer::UpdateSurface()
{
if (!mDirty)
return;
mDirty = PR_FALSE;
if (mGLContext) {
nsRefPtr<gfxImageSurface> isurf =
new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
(GetContentFlags() & CONTENT_OPAQUE)
? gfxASurface::ImageFormatRGB24
: gfxASurface::ImageFormatARGB32);
if (!isurf || isurf->CairoStatus() != 0) {
return;
}
NS_ASSERTION(isurf->Stride() == mBounds.width * 4, "gfxImageSurface stride isn't what we expect!");
// We need to read from the GLContext
mGLContext->MakeCurrent();
// We have to flush to ensure that any buffered GL operations are
// in the framebuffer before we read.
mGLContext->fFlush();
PRUint32 currentFramebuffer = 0;
mGLContext->fGetIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, (GLint*)&currentFramebuffer);
// Make sure that we read pixels from the correct framebuffer, regardless
// of what's currently bound.
if (currentFramebuffer != mCanvasFramebuffer)
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mCanvasFramebuffer);
mGLContext->ReadPixelsIntoImageSurface(0, 0,
mBounds.width, mBounds.height,
isurf);
// Put back the previous framebuffer binding.
if (currentFramebuffer != mCanvasFramebuffer)
mGLContext->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, currentFramebuffer);
// If the underlying GLContext doesn't have a framebuffer into which
// premultiplied values were written, we have to do this ourselves here.
// Note that this is a WebGL attribute; GL itself has no knowledge of
// premultiplied or unpremultiplied alpha.
if (!mGLBufferIsPremultiplied)
gfxUtils::PremultiplyImageSurface(isurf);
// stick our surface into mSurface, so that the Paint() path is the same
mSurface = isurf;
}
}
void
BasicCanvasLayer::Paint(gfxContext* aContext)
{
UpdateSurface();
PaintWithOpacity(aContext, GetEffectiveOpacity());
}
void
BasicCanvasLayer::PaintWithOpacity(gfxContext* aContext,
float aOpacity)
{
NS_ASSERTION(BasicManager()->InDrawing(),
"Can only draw in drawing phase");
nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
pat->SetFilter(mFilter);
pat->SetExtend(gfxPattern::EXTEND_PAD);
gfxMatrix m;
if (mNeedsYFlip) {
m = aContext->CurrentMatrix();
aContext->Translate(gfxPoint(0.0, mBounds.height));
aContext->Scale(1.0, -1.0);
}
aContext->NewPath();
// No need to snap here; our transform is already set up to snap our rect
aContext->Rectangle(gfxRect(0, 0, mBounds.width, mBounds.height));
aContext->SetPattern(pat);
aContext->FillWithOpacity(aOpacity);
if (mNeedsYFlip) {
aContext->SetMatrix(m);
}
}
class BasicReadbackLayer : public ReadbackLayer,
BasicImplData
{
public:
BasicReadbackLayer(BasicLayerManager* aLayerManager) :
ReadbackLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicReadbackLayer);
}
virtual ~BasicReadbackLayer()
{
MOZ_COUNT_DTOR(BasicReadbackLayer);
}
virtual void SetVisibleRegion(const nsIntRegion& aRegion)
{
NS_ASSERTION(BasicManager()->InConstruction(),
"Can only set properties in construction phase");
ReadbackLayer::SetVisibleRegion(aRegion);
}
protected:
BasicLayerManager* BasicManager()
{
return static_cast<BasicLayerManager*>(mManager);
}
};
static nsIntRect
ToOutsideIntRect(const gfxRect &aRect)
{
gfxRect r = aRect;
r.RoundOut();
return nsIntRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
}
static nsIntRect
ToInsideIntRect(const gfxRect& aRect)
{
gfxRect r = aRect;
r.RoundIn();
return nsIntRect(r.pos.x, r.pos.y, r.size.width, r.size.height);
}
/**
* Returns false if there is at most one leaf layer overlapping aBounds
* and that layer is opaque.
* aDirtyVisibleRegionInContainer is filled in only if we return false.
* It contains the union of the visible regions of leaf layers under aLayer.
*/
static PRBool
MayHaveOverlappingOrTransparentLayers(Layer* aLayer,
const nsIntRect& aBounds,
nsIntRegion* aDirtyVisibleRegionInContainer)
{
if (!(aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
return PR_TRUE;
}
gfxMatrix matrix;
if (!aLayer->GetTransform().Is2D(&matrix) ||
matrix.HasNonIntegerTranslation()) {
return PR_TRUE;
}
nsIntPoint translation = nsIntPoint(PRInt32(matrix.x0), PRInt32(matrix.y0));
nsIntRect bounds = aBounds - translation;
nsIntRect clippedDirtyRect = bounds;
const nsIntRect* clipRect = aLayer->GetClipRect();
if (clipRect) {
clippedDirtyRect.IntersectRect(clippedDirtyRect, *clipRect - translation);
}
aDirtyVisibleRegionInContainer->And(aLayer->GetVisibleRegion(), clippedDirtyRect);
aDirtyVisibleRegionInContainer->MoveBy(translation);
/* Ignore layers outside the clip rect */
if (aDirtyVisibleRegionInContainer->IsEmpty()) {
return PR_FALSE;
}
nsIntRegion region;
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
nsIntRegion childRegion;
if (MayHaveOverlappingOrTransparentLayers(child, bounds, &childRegion)) {
return PR_TRUE;
}
nsIntRegion tmp;
tmp.And(region, childRegion);
if (!tmp.IsEmpty()) {
return PR_TRUE;
}
region.Or(region, childRegion);
}
return PR_FALSE;
}
BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) :
#ifdef DEBUG
mPhase(PHASE_NONE),
#endif
mXResolution(1.0)
, mYResolution(1.0)
, mWidget(aWidget)
, mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(PR_FALSE)
, mTransactionIncomplete(false)
{
MOZ_COUNT_CTOR(BasicLayerManager);
NS_ASSERTION(aWidget, "Must provide a widget");
}
BasicLayerManager::BasicLayerManager() :
#ifdef DEBUG
mPhase(PHASE_NONE),
#endif
mWidget(nsnull)
, mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(PR_FALSE)
{
MOZ_COUNT_CTOR(BasicLayerManager);
}
BasicLayerManager::~BasicLayerManager()
{
NS_ASSERTION(!InTransaction(), "Died during transaction?");
ClearCachedResources();
mRoot = nsnull;
MOZ_COUNT_DTOR(BasicLayerManager);
}
void
BasicLayerManager::SetDefaultTarget(gfxContext* aContext,
BufferMode aDoubleBuffering)
{
NS_ASSERTION(!InTransaction(),
"Must set default target outside transaction");
mDefaultTarget = aContext;
mDoubleBuffering = aDoubleBuffering;
}
void
BasicLayerManager::BeginTransaction()
{
mUsingDefaultTarget = PR_TRUE;
BeginTransactionWithTarget(mDefaultTarget);
}
already_AddRefed<gfxContext>
BasicLayerManager::PushGroupWithCachedSurface(gfxContext *aTarget,
gfxASurface::gfxContentType aContent,
gfxPoint *aSavedOffset)
{
gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
aTarget->IdentityMatrix();
nsRefPtr<gfxASurface> currentSurf = aTarget->CurrentSurface();
gfxRect clip = aTarget->GetClipExtents();
clip.RoundOut();
nsRefPtr<gfxContext> ctx =
mCachedSurface.Get(aContent,
gfxIntSize(clip.size.width, clip.size.height),
currentSurf);
/* Align our buffer for the original surface */
ctx->Translate(-clip.pos);
*aSavedOffset = clip.pos;
ctx->Multiply(saveMatrix.Matrix());
return ctx.forget();
}
void
BasicLayerManager::PopGroupWithCachedSurface(gfxContext *aTarget,
const gfxPoint& aSavedOffset)
{
if (!mTarget)
return;
gfxContextMatrixAutoSaveRestore saveMatrix(aTarget);
aTarget->IdentityMatrix();
aTarget->SetSource(mTarget->OriginalSurface(), aSavedOffset);
aTarget->Paint();
}
void
BasicLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
{
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG(("[----- BeginTransaction"));
Log();
#endif
NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
#ifdef DEBUG
mPhase = PHASE_CONSTRUCTION;
#endif
mTarget = aTarget;
}
static void
TransformIntRect(nsIntRect& aRect, const gfxMatrix& aMatrix,
nsIntRect (*aRoundMethod)(const gfxRect&))
{
gfxRect gr = gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
gr = aMatrix.TransformBounds(gr);
aRect = (*aRoundMethod)(gr);
}
// This implementation assumes that GetEffectiveTransform transforms
// all layers to the same coordinate system. It can't be used as is
// by accelerated layers because of intermediate surfaces.
static void
MarkLeafLayersCoveredByOpaque(Layer* aLayer, const nsIntRect& aClipRect,
nsIntRegion& aRegion)
{
Layer* child = aLayer->GetLastChild();
BasicImplData* data = ToData(aLayer);
data->SetCoveredByOpaque(PR_FALSE);
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
nsIntRect newClipRect(aClipRect);
// Allow aLayer or aLayer's descendants to cover underlying layers
// only if it's opaque. GetEffectiveOpacity() could be used instead,
// but it does extra passes from descendant to ancestor.
if (aLayer->GetOpacity() != 1.0f) {
newClipRect.SetRect(0, 0, 0, 0);
}
if (clipRect) {
nsIntRect cr = *clipRect;
gfxMatrix tr;
if (aLayer->GetEffectiveTransform().Is2D(&tr)) {
TransformIntRect(cr, tr, ToInsideIntRect);
newClipRect.IntersectRect(newClipRect, cr);
} else {
newClipRect.SetRect(0, 0, 0, 0);
}
}
if (!child) {
gfxMatrix transform;
if (!aLayer->GetEffectiveTransform().Is2D(&transform)) {
return;
}
nsIntRegion region = aLayer->GetEffectiveVisibleRegion();
nsIntRect r = region.GetBounds();
TransformIntRect(r, transform, ToOutsideIntRect);
data->SetCoveredByOpaque(aRegion.Contains(r));
// Allow aLayer to cover underlying layers only if aLayer's
// content is opaque
if (!(aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
return;
}
nsIntRegionRectIterator it(region);
while (const nsIntRect* sr = it.Next()) {
r = *sr;
TransformIntRect(r, transform, ToInsideIntRect);
r.IntersectRect(r, newClipRect);
if (!r.IsEmpty()) {
aRegion.Or(aRegion, r);
}
}
} else {
for (; child; child = child->GetPrevSibling()) {
MarkLeafLayersCoveredByOpaque(child, newClipRect, aRegion);
}
}
}
void
BasicLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
EndTransactionInternal(aCallback, aCallbackData);
}
bool
BasicLayerManager::EndTransactionInternal(DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG((" ----- (beginning paint)"));
Log();
#endif
NS_ASSERTION(InConstruction(), "Should be in construction phase");
#ifdef DEBUG
mPhase = PHASE_DRAWING;
#endif
mTransactionIncomplete = false;
if (mTarget && mRoot) {
nsRefPtr<gfxContext> finalTarget = mTarget;
gfxPoint cachedSurfaceOffset;
nsIntRegion rootRegion;
PRBool useDoubleBuffering = mUsingDefaultTarget &&
mDoubleBuffering != BUFFER_NONE &&
MayHaveOverlappingOrTransparentLayers(mRoot,
ToOutsideIntRect(mTarget->GetClipExtents()),
&rootRegion);
if (useDoubleBuffering) {
nsRefPtr<gfxASurface> targetSurface = mTarget->CurrentSurface();
mTarget = PushGroupWithCachedSurface(mTarget, targetSurface->GetContentType(),
&cachedSurfaceOffset);
}
mSnapEffectiveTransforms =
!(mTarget->GetFlags() & gfxContext::FLAG_DISABLE_SNAPPING);
mRoot->ComputeEffectiveTransforms(gfx3DMatrix::From2D(mTarget->CurrentMatrix()));
nsIntRegion region;
MarkLeafLayersCoveredByOpaque(mRoot,
mRoot->GetEffectiveVisibleRegion().GetBounds(),
region);
PaintLayer(mRoot, aCallback, aCallbackData, nsnull);
// If we're doing manual double-buffering, we need to avoid drawing
// the results of an incomplete transaction to the destination surface.
// If the transaction is incomplete and we're not double-buffering then
// either the system is double-buffering our window (in which case the
// followup EndTransaction will be drawn over the top of our incomplete
// transaction before the system updates the window), or we have no
// overlapping or transparent layers in the update region, in which case
// our partial transaction drawing will look fine.
if (useDoubleBuffering && !mTransactionIncomplete) {
finalTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
PopGroupWithCachedSurface(finalTarget, cachedSurfaceOffset);
}
if (!mTransactionIncomplete) {
// Clear out target if we have a complete transaction.
mTarget = nsnull;
} else {
// If we don't have a complete transaction set back to the old mTarget.
mTarget = finalTarget;
}
}
#ifdef MOZ_LAYERS_HAVE_LOG
Log();
MOZ_LAYERS_LOG(("]----- EndTransaction"));
#endif
#ifdef DEBUG
// Go back to the construction phase if the transaction isn't complete.
// Layout will update the layer tree and call EndTransaction().
mPhase = mTransactionIncomplete ? PHASE_CONSTRUCTION : PHASE_NONE;
#endif
if (!mTransactionIncomplete) {
// This is still valid if the transaction was incomplete.
mUsingDefaultTarget = PR_FALSE;
}
NS_ASSERTION(!aCallback || !mTransactionIncomplete,
"If callback is not null, transaction must be complete");
// XXX - We should probably assert here that for an incomplete transaction
// out target is the default target.
return !mTransactionIncomplete;
}
bool
BasicLayerManager::EndEmptyTransaction()
{
if (!mRoot) {
return false;
}
return EndTransactionInternal(nsnull, nsnull);
}
void
BasicLayerManager::SetRoot(Layer* aLayer)
{
NS_ASSERTION(aLayer, "Root can't be null");
NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
mRoot = aLayer;
}
void
BasicLayerManager::PaintLayer(Layer* aLayer,
DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback)
{
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
const gfx3DMatrix& effectiveTransform = aLayer->GetEffectiveTransform();
PRBool needsGroup = aLayer->GetFirstChild() &&
static_cast<BasicContainerLayer*>(aLayer)->UseIntermediateSurface();
// If needsSaveRestore is false, we should still save and restore
// the CTM
PRBool needsSaveRestore = needsGroup || clipRect;
gfxMatrix savedMatrix;
if (needsSaveRestore) {
mTarget->Save();
if (clipRect) {
mTarget->NewPath();
mTarget->Rectangle(gfxRect(clipRect->x, clipRect->y, clipRect->width, clipRect->height), PR_TRUE);
mTarget->Clip();
}
} else {
savedMatrix = mTarget->CurrentMatrix();
}
gfxMatrix transform;
// XXX we need to add some kind of 3D transform support, possibly
// using pixman?
NS_ASSERTION(effectiveTransform.Is2D(),
"Only 2D transforms supported currently");
effectiveTransform.Is2D(&transform);
mTarget->SetMatrix(transform);
PRBool pushedTargetOpaqueRect = PR_FALSE;
const nsIntRegion& visibleRegion = aLayer->GetEffectiveVisibleRegion();
nsRefPtr<gfxASurface> currentSurface = mTarget->CurrentSurface();
const gfxRect& targetOpaqueRect = currentSurface->GetOpaqueRect();
// Try to annotate currentSurface with a region of pixels that have been
// (or will be) painted opaque, if no such region is currently set.
if (targetOpaqueRect.IsEmpty() && visibleRegion.GetNumRects() == 1 &&
(aLayer->GetContentFlags() & Layer::CONTENT_OPAQUE) &&
!transform.HasNonAxisAlignedTransform()) {
const nsIntRect& bounds = visibleRegion.GetBounds();
currentSurface->SetOpaqueRect(
mTarget->UserToDevice(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)));
pushedTargetOpaqueRect = PR_TRUE;
}
PRBool needsClipToVisibleRegion = PR_FALSE;
if (needsGroup) {
needsClipToVisibleRegion =
PushGroupForLayer(mTarget, aLayer, aLayer->GetEffectiveVisibleRegion());
}
/* Only paint ourself, or our children - This optimization relies on this! */
Layer* child = aLayer->GetFirstChild();
if (!child) {
BasicImplData* data = ToData(aLayer);
#ifdef MOZ_LAYERS_HAVE_LOG
MOZ_LAYERS_LOG(("%s (0x%p) is covered: %i\n", __FUNCTION__,
(void*)aLayer, data->IsCoveredByOpaque()));
#endif
if (!data->IsCoveredByOpaque()) {
if (aLayer->AsThebesLayer()) {
data->PaintThebes(mTarget, aCallback, aCallbackData, aReadback);
} else {
data->Paint(mTarget);
}
}
} else {
ReadbackProcessor readback;
if (IsRetained()) {
ContainerLayer* container = static_cast<ContainerLayer*>(aLayer);
readback.BuildUpdates(container);
}
for (; child; child = child->GetNextSibling()) {
PaintLayer(child, aCallback, aCallbackData, &readback);
if (mTransactionIncomplete)
break;
}
}
if (needsGroup) {
mTarget->PopGroupToSource();
if (needsClipToVisibleRegion) {
gfxUtils::ClipToRegion(mTarget, aLayer->GetEffectiveVisibleRegion());
}
mTarget->Paint(aLayer->GetEffectiveOpacity());
}
if (pushedTargetOpaqueRect) {
currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0));
}
if (needsSaveRestore) {
mTarget->Restore();
} else {
mTarget->SetMatrix(savedMatrix);
}
}
void
BasicLayerManager::ClearCachedResources()
{
if (mRoot) {
ClearLayer(mRoot);
}
mCachedSurface.Expire();
}
void
BasicLayerManager::ClearLayer(Layer* aLayer)
{
ToData(aLayer)->ClearCachedResources();
for (Layer* child = aLayer->GetFirstChild(); child;
child = child->GetNextSibling()) {
ClearLayer(child);
}
}
already_AddRefed<ThebesLayer>
BasicLayerManager::CreateThebesLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ThebesLayer> layer = new BasicThebesLayer(this);
return layer.forget();
}
already_AddRefed<ContainerLayer>
BasicLayerManager::CreateContainerLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ContainerLayer> layer = new BasicContainerLayer(this);
return layer.forget();
}
already_AddRefed<ImageLayer>
BasicLayerManager::CreateImageLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ImageLayer> layer = new BasicImageLayer(this);
return layer.forget();
}
already_AddRefed<ColorLayer>
BasicLayerManager::CreateColorLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ColorLayer> layer = new BasicColorLayer(this);
return layer.forget();
}
already_AddRefed<CanvasLayer>
BasicLayerManager::CreateCanvasLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<CanvasLayer> layer = new BasicCanvasLayer(this);
return layer.forget();
}
already_AddRefed<ReadbackLayer>
BasicLayerManager::CreateReadbackLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ReadbackLayer> layer = new BasicReadbackLayer(this);
return layer.forget();
}
#ifdef MOZ_IPC
class BasicShadowableThebesLayer;
class BasicShadowableLayer : public ShadowableLayer
{
public:
BasicShadowableLayer()
{
MOZ_COUNT_CTOR(BasicShadowableLayer);
}
~BasicShadowableLayer()
{
if (HasShadow()) {
PLayerChild::Send__delete__(GetShadow());
}
MOZ_COUNT_DTOR(BasicShadowableLayer);
}
void SetShadow(PLayerChild* aShadow)
{
NS_ABORT_IF_FALSE(!mShadow, "can't have two shadows (yet)");
mShadow = aShadow;
}
virtual void SetBackBufferImage(gfxSharedImageSurface* aBuffer)
{
NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
}
virtual PRBool SupportsSurfaceDescriptor() const { return PR_FALSE; }
virtual void SetBackBuffer(const SurfaceDescriptor& aBuffer)
{
NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
}
virtual void Disconnect()
{
// This is an "emergency Disconnect()", called when the compositing
// process has died. |mShadow| and our Shmem buffers are
// automatically managed by IPDL, so we don't need to explicitly
// free them here (it's hard to get that right on emergency
// shutdown anyway).
mShadow = nsnull;
}
virtual BasicShadowableThebesLayer* AsThebes() { return nsnull; }
};
static ShadowableLayer*
ToShadowable(Layer* aLayer)
{
return ToData(aLayer)->AsShadowableLayer();
}
// Some layers, like ReadbackLayers, can't be shadowed and shadowing
// them doesn't make sense anyway
static bool
ShouldShadow(Layer* aLayer)
{
if (!ToShadowable(aLayer)) {
NS_ABORT_IF_FALSE(aLayer->GetType() == Layer::TYPE_READBACK,
"Only expect not to shadow ReadbackLayers");
return false;
}
return true;
}
template<class OpT>
static BasicShadowableLayer*
GetBasicShadowable(const OpT& op)
{
return static_cast<BasicShadowableLayer*>(
static_cast<const ShadowLayerChild*>(op.layerChild())->layer());
}
class BasicShadowableContainerLayer : public BasicContainerLayer,
public BasicShadowableLayer {
public:
BasicShadowableContainerLayer(BasicShadowLayerManager* aManager) :
BasicContainerLayer(aManager)
{
MOZ_COUNT_CTOR(BasicShadowableContainerLayer);
}
virtual ~BasicShadowableContainerLayer()
{
MOZ_COUNT_DTOR(BasicShadowableContainerLayer);
}
virtual void InsertAfter(Layer* aChild, Layer* aAfter);
virtual void RemoveChild(Layer* aChild);
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ContainerLayerAttributes(GetFrameMetrics());
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect()
{
BasicShadowableLayer::Disconnect();
}
private:
BasicShadowLayerManager* ShadowManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
};
void
BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
{
if (HasShadow() && ShouldShadow(aChild)) {
while (aAfter && !ShouldShadow(aAfter)) {
aAfter = aAfter->GetPrevSibling();
}
ShadowManager()->InsertAfter(ShadowManager()->Hold(this),
ShadowManager()->Hold(aChild),
aAfter ? ShadowManager()->Hold(aAfter) : nsnull);
}
BasicContainerLayer::InsertAfter(aChild, aAfter);
}
void
BasicShadowableContainerLayer::RemoveChild(Layer* aChild)
{
if (HasShadow() && ShouldShadow(aChild)) {
ShadowManager()->RemoveChild(ShadowManager()->Hold(this),
ShadowManager()->Hold(aChild));
}
BasicContainerLayer::RemoveChild(aChild);
}
static PRBool
IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
{
return SurfaceDescriptor::T__None != aSurface.type();
}
class BasicShadowableThebesLayer : public BasicThebesLayer,
public BasicShadowableLayer
{
typedef BasicThebesLayer Base;
public:
BasicShadowableThebesLayer(BasicShadowLayerManager* aManager)
: BasicThebesLayer(aManager)
, mIsNewBuffer(false)
{
MOZ_COUNT_CTOR(BasicShadowableThebesLayer);
}
virtual ~BasicShadowableThebesLayer()
{
if (IsSurfaceDescriptorValid(mBackBuffer))
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
MOZ_COUNT_DTOR(BasicShadowableThebesLayer);
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ThebesLayerAttributes(GetValidRegion(),
mXResolution, mYResolution);
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual bool MustRetainContent() { return HasShadow(); }
virtual PRBool SupportsSurfaceDescriptor() const { return PR_TRUE; }
void SetBackBufferAndAttrs(const ThebesBuffer& aBuffer,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution,
const OptionalThebesBuffer& aReadOnlyFrontBuffer,
const nsIntRegion& aFrontUpdatedRegion);
virtual void Disconnect()
{
mBackBuffer = SurfaceDescriptor();
BasicShadowableLayer::Disconnect();
}
virtual BasicShadowableThebesLayer* AsThebes() { return this; }
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
NS_OVERRIDE virtual void
PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
PRBool aDidSelfCopy,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData);
NS_OVERRIDE virtual already_AddRefed<gfxASurface>
CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize);
// This describes the gfxASurface we hand to mBuffer. We keep a
// copy of the descriptor here so that we can call
// DestroySharedSurface() on the descriptor.
SurfaceDescriptor mBackBuffer;
PRPackedBool mIsNewBuffer;
};
void
BasicShadowableThebesLayer::SetBackBufferAndAttrs(const ThebesBuffer& aBuffer,
const nsIntRegion& aValidRegion,
float aXResolution,
float aYResolution,
const OptionalThebesBuffer& aReadOnlyFrontBuffer,
const nsIntRegion& aFrontUpdatedRegion)
{
mBackBuffer = aBuffer.buffer();
nsRefPtr<gfxASurface> backBuffer = BasicManager()->OpenDescriptor(mBackBuffer);
if (OptionalThebesBuffer::Tnull_t == aReadOnlyFrontBuffer.type()) {
// We didn't get back a read-only ref to our old back buffer (the
// parent's new front buffer). If the parent is pushing updates
// to a texture it owns, then we probably got back the same buffer
// we pushed in the update and all is well. If not, ...
mValidRegion = aValidRegion;
mXResolution = aXResolution;
mYResolution = aYResolution;
mBuffer.SetBackingBuffer(backBuffer, aBuffer.rect(), aBuffer.rotation());
return;
}
MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
this,
aFrontUpdatedRegion.GetBounds().x,
aFrontUpdatedRegion.GetBounds().y,
aFrontUpdatedRegion.GetBounds().width,
aFrontUpdatedRegion.GetBounds().height));
const ThebesBuffer roFront = aReadOnlyFrontBuffer.get_ThebesBuffer();
nsRefPtr<gfxASurface> roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer());
mBuffer.SetBackingBufferAndUpdateFrom(
backBuffer,
roFrontBuffer, roFront.rect(), roFront.rotation(),
aFrontUpdatedRegion, mXResolution, mYResolution);
// Now the new back buffer has the same (interesting) pixels as the
// new front buffer, and mValidRegion et al. are correct wrt the new
// back buffer (i.e. as they were for the old back buffer)
}
void
BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
PRBool aDidSelfCopy,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
Base::PaintBuffer(aContext,
aRegionToDraw, aExtendedRegionToDraw, aRegionToInvalidate,
aDidSelfCopy,
aCallback, aCallbackData);
if (!HasShadow()) {
return;
}
nsIntRegion updatedRegion;
if (mIsNewBuffer || aDidSelfCopy) {
// A buffer reallocation clears both buffers. The front buffer has all the
// content by now, but the back buffer is still clear. Here, in effect, we
// are saying to copy all of the pixels of the front buffer to the back.
// Also when we self-copied in the buffer, the buffer space
// changes and some changed buffer content isn't reflected in the
// draw or invalidate region (on purpose!). When this happens, we
// need to read back the entire buffer too.
updatedRegion = mVisibleRegion;
mIsNewBuffer = false;
} else {
updatedRegion = aRegionToDraw;
}
NS_ASSERTION(mBuffer.BufferRect().Contains(aRegionToDraw.GetBounds()),
"Update outside of buffer rect!");
NS_ABORT_IF_FALSE(IsSurfaceDescriptorValid(mBackBuffer),
"should have a back buffer by now");
BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this),
updatedRegion,
mBuffer.BufferRect(),
mBuffer.BufferRotation(),
mBackBuffer);
}
already_AddRefed<gfxASurface>
BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType,
const nsIntSize& aSize)
{
if (!HasShadow()) {
return BasicThebesLayer::CreateBuffer(aType, aSize);
}
MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): creating %d x %d buffer(x2)",
this,
aSize.width, aSize.height));
if (IsSurfaceDescriptorValid(mBackBuffer)) {
BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
mBackBuffer);
mBackBuffer = SurfaceDescriptor();
}
// XXX error handling
SurfaceDescriptor tmpFront;
if (BasicManager()->ShouldDoubleBuffer()) {
if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
aType,
&tmpFront,
&mBackBuffer)) {
NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
}
} else {
if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
aType,
&mBackBuffer)) {
NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
}
}
NS_ABORT_IF_FALSE(!mIsNewBuffer,
"Bad! Did we create a buffer twice without painting?");
mIsNewBuffer = true;
BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
nsIntRegion(),
1.0, 1.0,
nsIntRect(),
tmpFront);
return BasicManager()->OpenDescriptor(mBackBuffer);
}
class BasicShadowableImageLayer : public BasicImageLayer,
public BasicShadowableLayer
{
public:
BasicShadowableImageLayer(BasicShadowLayerManager* aManager) :
BasicImageLayer(aManager)
{
MOZ_COUNT_CTOR(BasicShadowableImageLayer);
}
virtual ~BasicShadowableImageLayer()
{
if (mBackSurface) {
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface);
}
MOZ_COUNT_DTOR(BasicShadowableImageLayer);
}
virtual void Paint(gfxContext* aContext);
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ImageLayerAttributes(mFilter);
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void SetBackBufferImage(gfxSharedImageSurface* aBuffer)
{
mBackSurface = aBuffer;
}
virtual void Disconnect()
{
mBackSurface = nsnull;
BasicShadowableLayer::Disconnect();
}
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
nsRefPtr<gfxSharedImageSurface> mBackSurface;
};
void
BasicShadowableImageLayer::Paint(gfxContext* aContext)
{
gfxIntSize oldSize = mSize;
nsRefPtr<gfxPattern> pat = GetAndPaintCurrentImage(aContext, GetEffectiveOpacity());
if (!pat || !HasShadow())
return;
if (oldSize != mSize) {
if (mBackSurface) {
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface);
mBackSurface = nsnull;
BasicManager()->DestroyedImageBuffer(BasicManager()->Hold(this));
}
nsRefPtr<gfxSharedImageSurface> tmpFrontSurface;
// XXX error handling?
if (!BasicManager()->AllocDoubleBuffer(
mSize,
(GetContentFlags() & CONTENT_OPAQUE) ?
gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA,
getter_AddRefs(tmpFrontSurface), getter_AddRefs(mBackSurface)))
NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
BasicManager()->CreatedImageBuffer(BasicManager()->Hold(this),
nsIntSize(mSize.width, mSize.height),
tmpFrontSurface);
}
nsRefPtr<gfxContext> tmpCtx = new gfxContext(mBackSurface);
PaintContext(pat,
nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
nsnull, 1.0, tmpCtx);
BasicManager()->PaintedImage(BasicManager()->Hold(this),
mBackSurface);
}
class BasicShadowableColorLayer : public BasicColorLayer,
public BasicShadowableLayer
{
public:
BasicShadowableColorLayer(BasicShadowLayerManager* aManager) :
BasicColorLayer(aManager)
{
MOZ_COUNT_CTOR(BasicShadowableColorLayer);
}
virtual ~BasicShadowableColorLayer()
{
MOZ_COUNT_DTOR(BasicShadowableColorLayer);
}
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ColorLayerAttributes(GetColor());
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect()
{
BasicShadowableLayer::Disconnect();
}
};
class BasicShadowableCanvasLayer : public BasicCanvasLayer,
public BasicShadowableLayer
{
public:
BasicShadowableCanvasLayer(BasicShadowLayerManager* aManager) :
BasicCanvasLayer(aManager)
{
MOZ_COUNT_CTOR(BasicShadowableCanvasLayer);
}
virtual ~BasicShadowableCanvasLayer()
{
if (mBackBuffer) {
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer);
}
MOZ_COUNT_DTOR(BasicShadowableCanvasLayer);
}
virtual void Initialize(const Data& aData);
virtual void Paint(gfxContext* aContext);
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = CanvasLayerAttributes(mFilter);
}
virtual Layer* AsLayer() { return this; }
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void SetBackBufferImage(gfxSharedImageSurface* aBuffer)
{
mBackBuffer = aBuffer;
}
virtual void Disconnect()
{
mBackBuffer = nsnull;
BasicShadowableLayer::Disconnect();
}
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
nsRefPtr<gfxSharedImageSurface> mBackBuffer;
};
void
BasicShadowableCanvasLayer::Initialize(const Data& aData)
{
BasicCanvasLayer::Initialize(aData);
if (!HasShadow())
return;
// XXX won't get here currently; need to figure out what to do on
// canvas resizes
if (mBackBuffer) {
BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer);
mBackBuffer = nsnull;
BasicManager()->DestroyedCanvasBuffer(BasicManager()->Hold(this));
}
nsRefPtr<gfxSharedImageSurface> tmpFrontBuffer;
// XXX error handling?
if (!BasicManager()->AllocDoubleBuffer(
gfxIntSize(aData.mSize.width, aData.mSize.height),
(GetContentFlags() & CONTENT_OPAQUE) ?
gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA,
getter_AddRefs(tmpFrontBuffer), getter_AddRefs(mBackBuffer)))
NS_RUNTIMEABORT("creating CanvasLayer back buffer failed!");
BasicManager()->CreatedCanvasBuffer(BasicManager()->Hold(this),
aData.mSize,
tmpFrontBuffer);
}
void
BasicShadowableCanvasLayer::Paint(gfxContext* aContext)
{
BasicCanvasLayer::Paint(aContext);
if (!HasShadow())
return;
// It'd be nice to draw directly into the shmem back buffer.
// Doing so is complex -- for 2D canvases, we'd need to copy
// changed areas, much like we do for Thebes layers, as well as
// do all sorts of magic to swap out the surface underneath the
// canvas' thebes/cairo context.
nsRefPtr<gfxContext> tmpCtx = new gfxContext(mBackBuffer);
tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
// call BasicCanvasLayer::Paint to draw to our tmp context, because
// it'll handle things like flipping correctly. We always want
// to do this with 1.0 opacity though, because opacity is a layer
// property that's handled by the shadow tree.
BasicCanvasLayer::PaintWithOpacity(tmpCtx, 1.0f);
BasicManager()->PaintedCanvas(BasicManager()->Hold(this),
mBackBuffer);
}
class ShadowThebesLayerBuffer : public BasicThebesLayerBuffer
{
typedef BasicThebesLayerBuffer Base;
public:
ShadowThebesLayerBuffer()
: Base(NULL)
{
MOZ_COUNT_CTOR(ShadowThebesLayerBuffer);
}
~ShadowThebesLayerBuffer()
{
MOZ_COUNT_DTOR(ShadowThebesLayerBuffer);
}
void Swap(gfxASurface* aNewBuffer,
const nsIntRect& aNewRect, const nsIntPoint& aNewRotation,
gfxASurface** aOldBuffer,
nsIntRect* aOldRect, nsIntPoint* aOldRotation)
{
*aOldRect = BufferRect();
*aOldRotation = BufferRotation();
gfxIntSize newSize = aNewBuffer->GetSize();
nsRefPtr<gfxASurface> oldBuffer;
oldBuffer = SetBuffer(aNewBuffer,
nsIntSize(newSize.width, newSize.height),
aNewRect, aNewRotation);
oldBuffer.forget(aOldBuffer);
}
protected:
virtual already_AddRefed<gfxASurface>
CreateBuffer(ContentType aType, const nsIntSize& aSize)
{
NS_RUNTIMEABORT("ShadowThebesLayer can't paint content");
return nsnull;
}
};
class BasicShadowThebesLayer : public ShadowThebesLayer, BasicImplData {
public:
BasicShadowThebesLayer(BasicShadowLayerManager* aLayerManager)
: ShadowThebesLayer(aLayerManager, static_cast<BasicImplData*>(this))
, mOldXResolution(1.0)
, mOldYResolution(1.0)
{
MOZ_COUNT_CTOR(BasicShadowThebesLayer);
}
virtual ~BasicShadowThebesLayer()
{
// If Disconnect() wasn't called on us, then we assume that the
// remote side shut down and IPC is disconnected, so we let IPDL
// clean up our front surface Shmem.
MOZ_COUNT_DTOR(BasicShadowThebesLayer);
}
virtual void SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution);
virtual void SetValidRegion(const nsIntRegion& aRegion)
{
mOldValidRegion = mValidRegion;
ShadowThebesLayer::SetValidRegion(aRegion);
}
virtual void SetResolution(float aXResolution, float aYResolution)
{
mOldXResolution = mXResolution;
mOldYResolution = mYResolution;
ShadowThebesLayer::SetResolution(aXResolution, aYResolution);
}
virtual void Disconnect()
{
DestroyFrontBuffer();
ShadowThebesLayer::Disconnect();
}
virtual void
Swap(const ThebesBuffer& aNewFront, const nsIntRegion& aUpdatedRegion,
ThebesBuffer* aNewBack, nsIntRegion* aNewBackValidRegion,
float* aNewXResolution, float* aNewYResolution,
OptionalThebesBuffer* aReadOnlyFront, nsIntRegion* aFrontUpdatedRegion);
virtual void DestroyFrontBuffer()
{
mFrontBuffer.Clear();
mValidRegion.SetEmpty();
mOldValidRegion.SetEmpty();
mOldXResolution = 1.0;
mOldYResolution = 1.0;
if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
BasicManager()->ShadowLayerManager::DestroySharedSurface(&mFrontBufferDescriptor, mAllocator);
}
}
virtual void PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback);
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
ShadowThebesLayerBuffer mFrontBuffer;
// Describes the gfxASurface we hand out to |mFrontBuffer|.
SurfaceDescriptor mFrontBufferDescriptor;
// When we receive an update from our remote partner, we stow away
// our previous parameters that described our previous front buffer.
// Then when we Swap() back/front buffers, we can return these
// parameters to our partner (adjusted as needed).
nsIntRegion mOldValidRegion;
float mOldXResolution;
float mOldYResolution;
};
void
BasicShadowThebesLayer::SetFrontBuffer(const OptionalThebesBuffer& aNewFront,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution)
{
mValidRegion = mOldValidRegion = aValidRegion;
mXResolution = mOldXResolution = aXResolution;
mYResolution = mOldYResolution = aYResolution;
NS_ABORT_IF_FALSE(OptionalThebesBuffer::Tnull_t != aNewFront.type(),
"aNewFront must be valid here!");
const ThebesBuffer newFront = aNewFront.get_ThebesBuffer();
nsRefPtr<gfxASurface> newFrontBuffer =
BasicManager()->OpenDescriptor(newFront.buffer());
nsRefPtr<gfxASurface> unused;
nsIntRect unusedRect;
nsIntPoint unusedRotation;
mFrontBuffer.Swap(newFrontBuffer, newFront.rect(), newFront.rotation(),
getter_AddRefs(unused), &unusedRect, &unusedRotation);
mFrontBufferDescriptor = newFront.buffer();
}
void
BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
const nsIntRegion& aUpdatedRegion,
ThebesBuffer* aNewBack,
nsIntRegion* aNewBackValidRegion,
float* aNewXResolution, float* aNewYResolution,
OptionalThebesBuffer* aReadOnlyFront,
nsIntRegion* aFrontUpdatedRegion)
{
// This code relies on Swap() arriving *after* attribute mutations.
aNewBack->buffer() = mFrontBufferDescriptor;
// We have to invalidate the pixels painted into the new buffer.
// They might overlap with our old pixels.
if (mOldXResolution == mXResolution && mOldYResolution == mYResolution) {
aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
} else {
// On resolution changes, pretend that our buffer has the new
// resolution, but just has no valid content. This can avoid
// unnecessary buffer reallocs.
//
// FIXME/bug 598866: when we start re-using buffers after
// resolution changes, we're going to need to implement
// front->back copies to avoid thrashing our valid region by
// always nullifying it.
aNewBackValidRegion->SetEmpty();
mOldXResolution = mXResolution;
mOldYResolution = mYResolution;
}
NS_ASSERTION(mXResolution == mOldXResolution && mYResolution == mOldYResolution,
"Uh-oh, buffer allocation thrash forthcoming!");
*aNewXResolution = mXResolution;
*aNewYResolution = mYResolution;
nsRefPtr<gfxASurface> newFrontBuffer =
BasicManager()->OpenDescriptor(aNewFront.buffer());
nsRefPtr<gfxASurface> unused;
mFrontBuffer.Swap(
newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
getter_AddRefs(unused), &aNewBack->rect(), &aNewBack->rotation());
mFrontBufferDescriptor = aNewFront.buffer();
*aReadOnlyFront = aNewFront;
*aFrontUpdatedRegion = aUpdatedRegion;
}
void
BasicShadowThebesLayer::PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback)
{
NS_ASSERTION(BasicManager()->InDrawing(),
"Can only draw in drawing phase");
NS_ASSERTION(BasicManager()->IsRetained(),
"ShadowThebesLayer makes no sense without retained mode");
if (!mFrontBuffer.GetBuffer()) {
return;
}
gfxContext* target = BasicManager()->GetTarget();
NS_ASSERTION(target, "We shouldn't be called if there's no target");
mFrontBuffer.DrawTo(this, target, GetEffectiveOpacity());
}
class BasicShadowContainerLayer : public ShadowContainerLayer, BasicImplData {
template<class Container>
friend void ContainerInsertAfter(Layer* aChild, Layer* aAfter, Container* aContainer);
template<class Container>
friend void ContainerRemoveChild(Layer* aChild, Container* aContainer);
public:
BasicShadowContainerLayer(BasicShadowLayerManager* aLayerManager) :
ShadowContainerLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicShadowContainerLayer);
}
virtual ~BasicShadowContainerLayer()
{
while (mFirstChild) {
ContainerRemoveChild(mFirstChild, this);
}
MOZ_COUNT_DTOR(BasicShadowContainerLayer);
}
virtual void InsertAfter(Layer* aChild, Layer* aAfter)
{ ContainerInsertAfter(aChild, aAfter, this); }
virtual void RemoveChild(Layer* aChild)
{ ContainerRemoveChild(aChild, this); }
virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
{
// We push groups for container layers if we need to, which always
// are aligned in device space, so it doesn't really matter how we snap
// containers.
gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), nsnull);
// We always pass the ideal matrix down to our children, so there is no
// need to apply any compensation using the residual from SnapTransform.
ComputeEffectiveTransformsForChildren(idealTransform);
/* If we have a single child, it can just inherit our opacity,
* otherwise we need a PushGroup and we need to mark ourselves as using
* an intermediate surface so our children don't inherit our opacity
* via GetEffectiveOpacity.
*/
mUseIntermediateSurface = GetEffectiveOpacity() != 1.0 && HasMultipleChildren();
}
};
class BasicShadowImageLayer : public ShadowImageLayer, BasicImplData {
public:
BasicShadowImageLayer(BasicShadowLayerManager* aLayerManager) :
ShadowImageLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicShadowImageLayer);
}
virtual ~BasicShadowImageLayer()
{
MOZ_COUNT_DTOR(BasicShadowImageLayer);
}
virtual void Disconnect()
{
DestroyFrontBuffer();
ShadowImageLayer::Disconnect();
}
virtual PRBool Init(gfxSharedImageSurface* front, const nsIntSize& size);
virtual already_AddRefed<gfxSharedImageSurface>
Swap(gfxSharedImageSurface* newFront);
virtual void DestroyFrontBuffer()
{
if (mFrontSurface) {
BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
}
mFrontSurface = nsnull;
}
virtual void Paint(gfxContext* aContext);
protected:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
// XXX ShmemImage?
nsRefPtr<gfxSharedImageSurface> mFrontSurface;
gfxIntSize mSize;
};
PRBool
BasicShadowImageLayer::Init(gfxSharedImageSurface* front,
const nsIntSize& size)
{
mFrontSurface = front;
mSize = gfxIntSize(size.width, size.height);
return PR_TRUE;
}
already_AddRefed<gfxSharedImageSurface>
BasicShadowImageLayer::Swap(gfxSharedImageSurface* newFront)
{
already_AddRefed<gfxSharedImageSurface> tmp = mFrontSurface.forget();
mFrontSurface = newFront;
return tmp;
}
void
BasicShadowImageLayer::Paint(gfxContext* aContext)
{
if (!mFrontSurface) {
return;
}
nsRefPtr<gfxPattern> pat = new gfxPattern(mFrontSurface);
pat->SetFilter(mFilter);
// The visible region can extend outside the image. If we're not
// tiling, we don't want to draw into that area, so just draw within
// the image bounds.
const nsIntRect* tileSrcRect = GetTileSourceRect();
BasicImageLayer::PaintContext(pat,
tileSrcRect ? GetEffectiveVisibleRegion() : nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
tileSrcRect,
GetEffectiveOpacity(), aContext);
}
class BasicShadowColorLayer : public ShadowColorLayer,
BasicImplData
{
public:
BasicShadowColorLayer(BasicShadowLayerManager* aLayerManager) :
ShadowColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicShadowColorLayer);
}
virtual ~BasicShadowColorLayer()
{
MOZ_COUNT_DTOR(BasicShadowColorLayer);
}
virtual void Paint(gfxContext* aContext)
{
BasicColorLayer::PaintColorTo(mColor, GetEffectiveOpacity(), aContext);
}
};
class BasicShadowCanvasLayer : public ShadowCanvasLayer,
BasicImplData
{
public:
BasicShadowCanvasLayer(BasicShadowLayerManager* aLayerManager) :
ShadowCanvasLayer(aLayerManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicShadowCanvasLayer);
}
virtual ~BasicShadowCanvasLayer()
{
MOZ_COUNT_DTOR(BasicShadowCanvasLayer);
}
virtual void Disconnect()
{
DestroyFrontBuffer();
ShadowCanvasLayer::Disconnect();
}
virtual void Initialize(const Data& aData);
virtual already_AddRefed<gfxSharedImageSurface>
Swap(gfxSharedImageSurface* newFront);
virtual void DestroyFrontBuffer()
{
if (mFrontSurface) {
BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface, mAllocator);
}
mFrontSurface = nsnull;
}
virtual void Paint(gfxContext* aContext);
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
nsRefPtr<gfxSharedImageSurface> mFrontSurface;
};
void
BasicShadowCanvasLayer::Initialize(const Data& aData)
{
NS_ASSERTION(mFrontSurface == nsnull,
"BasicCanvasLayer::Initialize called twice!");
NS_ASSERTION(aData.mSurface && !aData.mGLContext, "no comprende OpenGL!");
mFrontSurface = static_cast<gfxSharedImageSurface*>(aData.mSurface);
mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
}
already_AddRefed<gfxSharedImageSurface>
BasicShadowCanvasLayer::Swap(gfxSharedImageSurface* newFront)
{
already_AddRefed<gfxSharedImageSurface> tmp = mFrontSurface.forget();
mFrontSurface = newFront;
return tmp;
}
void
BasicShadowCanvasLayer::Paint(gfxContext* aContext)
{
NS_ASSERTION(BasicManager()->InDrawing(),
"Can only draw in drawing phase");
if (!mFrontSurface) {
return;
}
nsRefPtr<gfxPattern> pat = new gfxPattern(mFrontSurface);
pat->SetFilter(mFilter);
pat->SetExtend(gfxPattern::EXTEND_PAD);
gfxRect r(0, 0, mBounds.width, mBounds.height);
aContext->NewPath();
// No need to snap here; our transform has already taken care of it
aContext->Rectangle(r);
aContext->SetPattern(pat);
aContext->FillWithOpacity(GetEffectiveOpacity());
}
// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding
// our layer tree to a parent process. Record the new layer creation
// in the current open transaction as a side effect.
template<typename CreatedMethod>
static void
MaybeCreateShadowFor(BasicShadowableLayer* aLayer,
BasicShadowLayerManager* aMgr,
CreatedMethod aMethod)
{
if (!aMgr->HasShadowManager()) {
return;
}
PLayerChild* shadow = aMgr->ConstructShadowFor(aLayer);
// XXX error handling
NS_ABORT_IF_FALSE(shadow, "failed to create shadow");
aLayer->SetShadow(shadow);
(aMgr->*aMethod)(aLayer);
aMgr->Hold(aLayer->AsLayer());
}
#define MAYBE_CREATE_SHADOW(_type) \
MaybeCreateShadowFor(layer, this, \
&ShadowLayerForwarder::Created ## _type ## Layer)
already_AddRefed<ThebesLayer>
BasicShadowLayerManager::CreateThebesLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableThebesLayer> layer =
new BasicShadowableThebesLayer(this);
MAYBE_CREATE_SHADOW(Thebes);
return layer.forget();
}
already_AddRefed<ContainerLayer>
BasicShadowLayerManager::CreateContainerLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableContainerLayer> layer =
new BasicShadowableContainerLayer(this);
MAYBE_CREATE_SHADOW(Container);
return layer.forget();
}
already_AddRefed<ImageLayer>
BasicShadowLayerManager::CreateImageLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableImageLayer> layer =
new BasicShadowableImageLayer(this);
MAYBE_CREATE_SHADOW(Image);
return layer.forget();
}
already_AddRefed<ColorLayer>
BasicShadowLayerManager::CreateColorLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableColorLayer> layer =
new BasicShadowableColorLayer(this);
MAYBE_CREATE_SHADOW(Color);
return layer.forget();
}
already_AddRefed<CanvasLayer>
BasicShadowLayerManager::CreateCanvasLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableCanvasLayer> layer =
new BasicShadowableCanvasLayer(this);
MAYBE_CREATE_SHADOW(Canvas);
return layer.forget();
}
already_AddRefed<ShadowThebesLayer>
BasicShadowLayerManager::CreateShadowThebesLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ShadowThebesLayer> layer = new BasicShadowThebesLayer(this);
return layer.forget();
}
already_AddRefed<ShadowContainerLayer>
BasicShadowLayerManager::CreateShadowContainerLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ShadowContainerLayer> layer = new BasicShadowContainerLayer(this);
return layer.forget();
}
already_AddRefed<ShadowImageLayer>
BasicShadowLayerManager::CreateShadowImageLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ShadowImageLayer> layer = new BasicShadowImageLayer(this);
return layer.forget();
}
already_AddRefed<ShadowColorLayer>
BasicShadowLayerManager::CreateShadowColorLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ShadowColorLayer> layer = new BasicShadowColorLayer(this);
return layer.forget();
}
already_AddRefed<ShadowCanvasLayer>
BasicShadowLayerManager::CreateShadowCanvasLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<ShadowCanvasLayer> layer = new BasicShadowCanvasLayer(this);
return layer.forget();
}
BasicShadowLayerManager::BasicShadowLayerManager(nsIWidget* aWidget) :
BasicLayerManager(aWidget)
{
MOZ_COUNT_CTOR(BasicShadowLayerManager);
}
BasicShadowLayerManager::~BasicShadowLayerManager()
{
MOZ_COUNT_DTOR(BasicShadowLayerManager);
}
void
BasicShadowLayerManager::SetRoot(Layer* aLayer)
{
if (mRoot != aLayer) {
if (HasShadowManager()) {
// Have to hold the old root and its children in order to
// maintain the same view of the layer tree in this process as
// the parent sees. Otherwise layers can be destroyed
// mid-transaction and bad things can happen (v. bug 612573)
if (mRoot) {
Hold(mRoot);
}
ShadowLayerForwarder::SetRoot(Hold(aLayer));
}
BasicLayerManager::SetRoot(aLayer);
}
}
void
BasicShadowLayerManager::Mutated(Layer* aLayer)
{
BasicLayerManager::Mutated(aLayer);
NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase");
if (HasShadowManager() && ShouldShadow(aLayer)) {
ShadowLayerForwarder::Mutated(Hold(aLayer));
}
}
void
BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
{
NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?");
// If the last transaction was incomplete (a failed DoEmptyTransaction),
// don't signal a new transaction to ShadowLayerForwarder. Carry on adding
// to the previous transaction.
if (HasShadowManager()) {
ShadowLayerForwarder::BeginTransaction();
}
BasicLayerManager::BeginTransactionWithTarget(aTarget);
}
void
BasicShadowLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
BasicLayerManager::EndTransaction(aCallback, aCallbackData);
ForwardTransaction();
}
bool
BasicShadowLayerManager::EndEmptyTransaction()
{
if (!BasicLayerManager::EndEmptyTransaction()) {
// Return without calling ForwardTransaction. This leaves the
// ShadowLayerForwarder transaction open; the following
// EndTransaction will complete it.
return false;
}
ForwardTransaction();
return true;
}
void
BasicShadowLayerManager::ForwardTransaction()
{
#ifdef DEBUG
mPhase = PHASE_FORWARD;
#endif
// forward this transaction's changeset to our ShadowLayerManager
AutoInfallibleTArray<EditReply, 10> replies;
if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) {
for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
const EditReply& reply = replies[i];
switch (reply.type()) {
case EditReply::TOpThebesBufferSwap: {
MOZ_LAYERS_LOG(("[LayersForwarder] ThebesBufferSwap"));
const OpThebesBufferSwap& obs = reply.get_OpThebesBufferSwap();
BasicShadowableThebesLayer* thebes = GetBasicShadowable(obs)->AsThebes();
thebes->SetBackBufferAndAttrs(
obs.newBackBuffer(),
obs.newValidRegion(), obs.newXResolution(), obs.newYResolution(),
obs.readOnlyFrontBuffer(), obs.frontUpdatedRegion());
break;
}
case EditReply::TOpBufferSwap: {
MOZ_LAYERS_LOG(("[LayersForwarder] BufferSwap"));
const OpBufferSwap& obs = reply.get_OpBufferSwap();
const SurfaceDescriptor& descr = obs.newBackBuffer();
BasicShadowableLayer* layer = GetBasicShadowable(obs);
if (layer->SupportsSurfaceDescriptor()) {
layer->SetBackBuffer(descr);
} else {
if (SurfaceDescriptor::TShmem != descr.type()) {
NS_RUNTIMEABORT("non-Shmem surface sent to a layer that expected one!");
}
nsRefPtr<gfxASurface> imageSurf = OpenDescriptor(descr);
layer->SetBackBufferImage(
static_cast<gfxSharedImageSurface*>(imageSurf.get()));
}
break;
}
default:
NS_RUNTIMEABORT("not reached");
}
}
} else if (HasShadowManager()) {
NS_WARNING("failed to forward Layers transaction");
}
#ifdef DEBUG
mPhase = PHASE_NONE;
#endif
// this may result in Layers being deleted, which results in
// PLayer::Send__delete__() and DeallocShmem()
mKeepAlive.Clear();
}
ShadowableLayer*
BasicShadowLayerManager::Hold(Layer* aLayer)
{
NS_ABORT_IF_FALSE(HasShadowManager(),
"top-level tree, no shadow tree to remote to");
ShadowableLayer* shadowable = ToShadowable(aLayer);
NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer");
mKeepAlive.AppendElement(aLayer);
return shadowable;
}
PRBool
BasicShadowLayerManager::IsCompositingCheap()
{
// Whether compositing is cheap depends on the parent backend.
return mShadowManager &&
LayerManager::IsCompositingCheap(GetParentBackendType());
}
#endif // MOZ_IPC
}
}