gecko/gfx/layers/basic/BasicLayers.cpp

2886 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();
FireDidTransactionCallback();
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
}
}