--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -55,16 +55,19 @@
#include "gfxMatrix.h"
#ifdef MOZ_SVG
#include "nsSVGIntegrationUtils.h"
#endif
#include "nsLayoutUtils.h"
#include "imgIContainer.h"
#include "nsIInterfaceRequestorUtils.h"
+#include "BasicLayers.h"
+
+using namespace mozilla::layers;
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
PRBool aIsForEvents, PRBool aBuildCaret)
: mReferenceFrame(aReferenceFrame),
mMovingFrame(nsnull),
mSaveVisibleRegionOfMovingContent(nsnull),
mIgnoreScrollFrame(nsnull),
mCurrentTableItem(nsnull),
@@ -396,30 +399,440 @@ nsDisplayList::ComputeVisibility(nsDispl
}
}
aBuilder->AccumulateVisibleRegionOfMovingContent(
nsRegion(movingContentAccumulatedBounds),
movingContentVisibleRegionBeforeMove,
movingContentVisibleRegionAfterMove);
+ mIsOpaque = aVisibleRegion->IsEmpty();
#ifdef DEBUG
mDidComputeVisibility = PR_TRUE;
#endif
}
+namespace {
+/**
+ * This class iterates through a display list tree, descending only into
+ * nsDisplayClip items, and returns each display item encountered during
+ * such iteration. Along with each item we also return the clip rect
+ * accumulated for the item.
+ */
+class ClippedItemIterator {
+public:
+ ClippedItemIterator(const nsDisplayList* aList)
+ {
+ DescendIntoList(aList, nsnull, nsnull);
+ AdvanceToItem();
+ }
+ PRBool IsDone()
+ {
+ return mStack.IsEmpty();
+ }
+ void Next()
+ {
+ State* top = StackTop();
+ top->mItem = top->mItem->GetAbove();
+ AdvanceToItem();
+ }
+ // Returns null if there is no clipping affecting the item. The
+ // clip rect is in device pixels
+ const gfxRect* GetEffectiveClipRect()
+ {
+ State* top = StackTop();
+ return top->mHasClipRect ? &top->mEffectiveClipRect : nsnull;
+ }
+ nsDisplayItem* Item()
+ {
+ return StackTop()->mItem;
+ }
+
+private:
+ // We maintain a stack of state objects. Each State object represents
+ // where we're up to in the iteration of a list.
+ struct State {
+ // The current item we're at in the list
+ nsDisplayItem* mItem;
+ // The effective clip rect applying to all the items in this list
+ gfxRect mEffectiveClipRect;
+ PRPackedBool mHasClipRect;
+ };
+
+ State* StackTop()
+ {
+ return &mStack[mStack.Length() - 1];
+ }
+ void DescendIntoList(const nsDisplayList* aList,
+ nsPresContext* aPresContext,
+ const nsRect* aClipRect)
+ {
+ State* state = mStack.AppendElement();
+ if (!state)
+ return;
+ if (mStack.Length() >= 2) {
+ *state = mStack[mStack.Length() - 2];
+ } else {
+ state->mHasClipRect = PR_FALSE;
+ }
+ state->mItem = aList->GetBottom();
+ if (aClipRect) {
+ gfxRect r(aClipRect->x, aClipRect->y, aClipRect->width, aClipRect->height);
+ r.ScaleInverse(aPresContext->AppUnitsPerDevPixel());
+ if (state->mHasClipRect) {
+ state->mEffectiveClipRect = state->mEffectiveClipRect.Intersect(r);
+ } else {
+ state->mEffectiveClipRect = r;
+ state->mHasClipRect = PR_TRUE;
+ }
+ }
+ }
+ // Advances to an item that the iterator should return.
+ void AdvanceToItem()
+ {
+ while (!mStack.IsEmpty()) {
+ State* top = StackTop();
+ if (!top->mItem) {
+ mStack.SetLength(mStack.Length() - 1);
+ if (!mStack.IsEmpty()) {
+ top = StackTop();
+ top->mItem = top->mItem->GetAbove();
+ }
+ continue;
+ }
+ if (top->mItem->GetType() != nsDisplayItem::TYPE_CLIP)
+ return;
+ nsDisplayClip* clipItem = static_cast<nsDisplayClip*>(top->mItem);
+ nsRect clip = clipItem->GetClipRect();
+ DescendIntoList(clipItem->GetList(),
+ clipItem->GetClippingFrame()->PresContext(),
+ &clip);
+ }
+ }
+
+ nsAutoTArray<State,10> mStack;
+};
+}
+
+/**
+ * Given a (possibly clipped) display item in aItem, try to append it to
+ * the items in aGroup. If aItem is the next item in the sublist in
+ * aGroup, and the clipping matches, we can just update aGroup in-place,
+ * otherwise we'll allocate a new ItemGroup, add it to the linked list,
+ * and put aItem in the new ItemGroup. We return the ItemGroup into which
+ * aItem was inserted.
+ */
+static nsDisplayList::ItemGroup*
+AddToItemGroup(nsDisplayListBuilder* aBuilder,
+ nsDisplayList::ItemGroup* aGroup, nsDisplayItem* aItem,
+ const gfxRect* aClipRect) {
+ NS_ASSERTION(!aGroup->mNextItemsForLayer,
+ "aGroup must be the last group in the chain");
+
+ if (!aGroup->mStartItem) {
+ aGroup->mStartItem = aItem;
+ aGroup->mEndItem = aItem->GetAbove();
+ aGroup->mHasClipRect = aClipRect != nsnull;
+ if (aClipRect) {
+ aGroup->mClipRect = *aClipRect;
+ }
+ return aGroup;
+ }
+
+ if (aGroup->mEndItem == aItem &&
+ (aGroup->mHasClipRect
+ ? (aClipRect && aGroup->mClipRect == *aClipRect)
+ : !aClipRect)) {
+ aGroup->mEndItem = aItem->GetAbove();
+ return aGroup;
+ }
+
+ nsDisplayList::ItemGroup* itemGroup =
+ new (aBuilder) nsDisplayList::ItemGroup();
+ if (!itemGroup)
+ return aGroup;
+ aGroup->mNextItemsForLayer = itemGroup;
+ return AddToItemGroup(aBuilder, itemGroup, aItem, aClipRect);
+}
+
+/**
+ * Create an empty Thebes layer, with an empty ItemGroup associated with
+ * it, and append it to aLayers.
+ */
+static nsDisplayList::ItemGroup*
+CreateEmptyThebesLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsTArray<nsDisplayList::LayerItems>* aLayers) {
+ nsDisplayList::ItemGroup* itemGroup =
+ new (aBuilder) nsDisplayList::ItemGroup();
+ if (!itemGroup)
+ return nsnull;
+ nsRefPtr<ThebesLayer> thebesLayer =
+ aManager->CreateThebesLayer();
+ if (!thebesLayer)
+ return nsnull;
+ nsDisplayList::LayerItems* layerItems =
+ aLayers->AppendElement(nsDisplayList::LayerItems(itemGroup));
+ layerItems->mThebesLayer = thebesLayer;
+ layerItems->mLayer = thebesLayer.forget();
+ return itemGroup;
+}
+
+/**
+ * This is the heart of layout's integration with layers. We
+ * use a ClippedItemIterator to iterate through descendant display
+ * items. Each item either has its own layer or is assigned to a
+ * ThebesLayer. We create ThebesLayers as necessary, although we try
+ * to put items in the bottom-most ThebesLayer because that is most
+ * likely to be able to render with an opaque background, which will often
+ * be required for subpixel text antialiasing to work.
+ */
+void nsDisplayList::BuildLayers(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsTArray<LayerItems>* aLayers) const {
+ NS_ASSERTION(aLayers->IsEmpty(), "aLayers must be initially empty");
+
+ // Create "bottom" Thebes layer. We'll try to put as much content
+ // as possible in this layer because if the container is filled with
+ // opaque content, this bottommost layer can also be treated as opaque,
+ // which means content in this layer can have subpixel AA.
+ // firstThebesLayerItems always points to the last ItemGroup for the
+ // first Thebes layer.
+ ItemGroup* firstThebesLayerItems =
+ CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
+ if (!firstThebesLayerItems)
+ return;
+ // lastThebesLayerItems always points to the last ItemGroup for the
+ // topmost layer, if it's a ThebesLayer. If the top layer is not a
+ // Thebes layer, this is null.
+ ItemGroup* lastThebesLayerItems = firstThebesLayerItems;
+ // This region contains the bounds of all the content that is above
+ // the first Thebes layer.
+ nsRegion areaAboveFirstThebesLayer;
+
+ for (ClippedItemIterator iter(this); !iter.IsDone(); iter.Next()) {
+ nsDisplayItem* item = iter.Item();
+ const gfxRect* clipRect = iter.GetEffectiveClipRect();
+ // Ask the item if it manages its own layer
+ nsRefPtr<Layer> layer = item->BuildLayer(aBuilder, aManager);
+ nsRect bounds = item->GetBounds(aBuilder);
+ // We set layerItems to point to the LayerItems object where the
+ // item ends up.
+ LayerItems* layerItems = nsnull;
+ if (layer) {
+ // item has a dedicated layer. Add it to the list, with an ItemGroup
+ // covering this item only.
+ ItemGroup* itemGroup = new (aBuilder) ItemGroup();
+ if (itemGroup) {
+ AddToItemGroup(aBuilder, itemGroup, item, clipRect);
+ layerItems = aLayers->AppendElement(LayerItems(itemGroup));
+ if (layerItems) {
+ if (itemGroup->mHasClipRect) {
+ gfxRect r = itemGroup->mClipRect;
+ r.Round();
+ nsIntRect intRect(r.X(), r.Y(), r.Width(), r.Height());
+ layer->IntersectClipRect(intRect);
+ }
+ layerItems->mLayer = layer.forget();
+ }
+ }
+ // This item is above the first Thebes layer.
+ areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+ lastThebesLayerItems = nsnull;
+ } else {
+ // No dedicated layer. Add it to a Thebes layer. First try to add
+ // it to the first Thebes layer, which we can do if there's no
+ // content between the first Thebes layer and our display item that
+ // overlaps our display item.
+ if (!areaAboveFirstThebesLayer.Intersects(bounds)) {
+ firstThebesLayerItems =
+ AddToItemGroup(aBuilder, firstThebesLayerItems, item, clipRect);
+ layerItems = &aLayers->ElementAt(0);
+ } else if (lastThebesLayerItems) {
+ // Try to add to the last Thebes layer
+ lastThebesLayerItems =
+ AddToItemGroup(aBuilder, lastThebesLayerItems, item, clipRect);
+ // This item is above the first Thebes layer.
+ areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+ layerItems = &aLayers->ElementAt(aLayers->Length() - 1);
+ } else {
+ // Create a new Thebes layer
+ ItemGroup* itemGroup =
+ CreateEmptyThebesLayer(aBuilder, aManager, aLayers);
+ if (itemGroup) {
+ lastThebesLayerItems =
+ AddToItemGroup(aBuilder, itemGroup, item, clipRect);
+ NS_ASSERTION(lastThebesLayerItems == itemGroup,
+ "AddToItemGroup shouldn't create a new group if the "
+ "initial group is empty");
+ // This item is above the first Thebes layer.
+ areaAboveFirstThebesLayer.Or(areaAboveFirstThebesLayer, bounds);
+ }
+ }
+ }
+
+ if (layerItems) {
+ // Update the visible region of the layer to account for the new
+ // item
+ nscoord appUnitsPerDevPixel =
+ item->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
+ layerItems->mVisibleRect.UnionRect(layerItems->mVisibleRect,
+ item->mVisibleRect.ToOutsidePixels(appUnitsPerDevPixel));
+ layerItems->mLayer->SetVisibleRegion(nsIntRegion(layerItems->mVisibleRect));
+ }
+ }
+
+ if (!firstThebesLayerItems->mStartItem) {
+ // The first Thebes layer has nothing in it. Delete the layer.
+ aLayers->RemoveElementAt(0);
+ }
+}
+
+/**
+ * We build a single layer by first building a list of layers needed for
+ * all the display items, and then if there's not just one layer in the
+ * list, we build a container layer to hold them.
+ */
+already_AddRefed<Layer>
+nsDisplayList::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsTArray<LayerItems>* aLayers) const {
+ BuildLayers(aBuilder, aManager, aLayers);
+
+ nsRefPtr<Layer> layer;
+ if (aLayers->Length() == 1) {
+ // We can just return the one layer
+ layer = aLayers->ElementAt(0).mLayer;
+ } else {
+ // We need to group multiple layers together into a container
+ nsRefPtr<ContainerLayer> container =
+ aManager->CreateContainerLayer();
+ if (!container)
+ return nsnull;
+
+ Layer* lastChild = nsnull;
+ nsIntRect visibleRect;
+ for (PRUint32 i = 0; i < aLayers->Length(); ++i) {
+ LayerItems* layerItems = &aLayers->ElementAt(i);
+ visibleRect.UnionRect(visibleRect, layerItems->mVisibleRect);
+ Layer* child = layerItems->mLayer;
+ container->InsertAfter(child, lastChild);
+ lastChild = child;
+ }
+ container->SetVisibleRegion(nsIntRegion(visibleRect));
+ layer = container.forget();
+ }
+ layer->SetIsOpaqueContent(mIsOpaque);
+ return layer.forget();
+}
+
+/**
+ * We paint by executing a layer manager transaction, constructing a
+ * single layer representing the display list, and then making it the
+ * root of the layer manager, drawing into the ThebesLayers.
+ */
void nsDisplayList::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx) const {
NS_ASSERTION(mDidComputeVisibility,
"Must call ComputeVisibility before calling Paint");
- for (nsDisplayItem* i = GetBottom(); i != nsnull; i = i->GetAbove()) {
- i->Paint(aBuilder, aCtx);
+ // To paint a display list, we construct a BasicLayerManager and
+ // a layer tree for it, and ask the BasicLayerManager to render.
+ nsRefPtr<LayerManager> layerManager = new BasicLayerManager();
+ if (!layerManager)
+ return;
+ layerManager->BeginTransactionWithTarget(aCtx->ThebesContext());
+
+ nsAutoTArray<LayerItems,10> layers;
+ nsRefPtr<Layer> root = BuildLayer(aBuilder, layerManager, &layers);
+ if (!root)
+ return;
+
+ layerManager->SetRoot(root);
+ layerManager->EndConstruction();
+ PaintThebesLayers(aBuilder, layers);
+ layerManager->EndTransaction();
+
+ nsCSSRendering::DidPaint();
+}
+
+void
+nsDisplayList::PaintThebesLayers(nsDisplayListBuilder* aBuilder,
+ const nsTArray<LayerItems>& aLayers) const
+{
+ for (PRUint32 i = 0; i < aLayers.Length(); ++i) {
+ ThebesLayer* thebesLayer = aLayers[i].mThebesLayer;
+ if (!thebesLayer) {
+ // Just ask the display item to paint any Thebes layers that it
+ // used to construct its layer
+ aLayers[i].mItems->mStartItem->PaintThebesLayers(aBuilder);
+ continue;
+ }
+
+ // OK, we have a real Thebes layer. Start drawing into it.
+ nsIntRegion toDraw;
+ gfxContext* ctx = thebesLayer->BeginDrawing(&toDraw);
+ if (!ctx)
+ continue;
+ // For now, we'll ignore toDraw and just draw the entire visible
+ // area, because the "visible area" is already confined to just the
+ // area that needs to be repainted. Later, when we start reusing layers
+ // from paint to paint, we'll need to pay attention to toDraw and
+ // actually try to avoid drawing stuff that's not in it.
+
+ // Our list may contain content with different prescontexts at
+ // different zoom levels. 'rc' contains the nsIRenderingContext
+ // used for the previous display item, and lastPresContext is the
+ // prescontext for that item. We also cache the clip state for that
+ // item.
+ nsRefPtr<nsIRenderingContext> rc;
+ nsPresContext* lastPresContext = nsnull;
+ gfxRect currentClip;
+ PRBool setClipRect = PR_FALSE;
+ NS_ASSERTION(aLayers[i].mItems, "No empty layers allowed");
+ for (ItemGroup* group = aLayers[i].mItems; group;
+ group = group->mNextItemsForLayer) {
+ // If the new desired clip state is different from the current state,
+ // update the clip.
+ if (setClipRect != group->mHasClipRect ||
+ (group->mHasClipRect && group->mClipRect != currentClip)) {
+ if (setClipRect) {
+ ctx->Restore();
+ }
+ setClipRect = group->mHasClipRect;
+ if (setClipRect) {
+ ctx->Save();
+ ctx->NewPath();
+ ctx->Rectangle(group->mClipRect, PR_TRUE);
+ ctx->Clip();
+ currentClip = group->mClipRect;
+ }
+ }
+ NS_ASSERTION(group->mStartItem, "No empty groups allowed");
+ for (nsDisplayItem* item = group->mStartItem; item != group->mEndItem;
+ item = item->GetAbove()) {
+ nsPresContext* presContext = item->GetUnderlyingFrame()->PresContext();
+ if (presContext != lastPresContext) {
+ // Create a new rendering context with the right
+ // appunits-per-dev-pixel.
+ nsresult rv =
+ presContext->DeviceContext()->CreateRenderingContextInstance(*getter_AddRefs(rc));
+ if (NS_FAILED(rv))
+ break;
+ rc->Init(presContext->DeviceContext(), ctx);
+ lastPresContext = presContext;
+ }
+ item->Paint(aBuilder, rc);
+ }
+ }
+ if (setClipRect) {
+ ctx->Restore();
+ }
+ thebesLayer->EndDrawing();
}
- nsCSSRendering::DidPaint();
}
PRUint32 nsDisplayList::Count() const {
PRUint32 count = 0;
for (nsDisplayItem* i = GetBottom(); i; i = i->GetAbove()) {
++count;
}
return count;
@@ -991,17 +1404,17 @@ PRBool nsDisplayWrapList::IsVaryingRelat
// this on wrapped lists.
NS_WARNING("nsDisplayWrapList::IsVaryingRelativeToMovingFrame called unexpectedly");
// We could try to do something but let's conservatively just return PR_TRUE.
return PR_TRUE;
}
void nsDisplayWrapList::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx) {
- mList.Paint(aBuilder, aCtx);
+ NS_ERROR("nsDisplayWrapList should have been flattened away for painting");
}
static nsresult
WrapDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, nsDisplayWrapper* aWrapper) {
if (!aList->GetTop() && !aBuilder->HasMovingFrames())
return NS_OK;
nsDisplayItem* item = aWrapper->WrapList(aBuilder, aFrame, aList);
@@ -1066,93 +1479,71 @@ nsresult nsDisplayWrapper::WrapListsInPl
// The positioned descendants may not be in-flow
rv = WrapEachDisplayItem(aBuilder, aLists.PositionedDescendants(), this);
NS_ENSURE_SUCCESS(rv, rv);
// The outlines may not be in-flow
return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
}
nsDisplayOpacity::nsDisplayOpacity(nsIFrame* aFrame, nsDisplayList* aList)
- : nsDisplayWrapList(aFrame, aList), mNeedAlpha(PR_TRUE) {
+ : nsDisplayWrapList(aFrame, aList) {
MOZ_COUNT_CTOR(nsDisplayOpacity);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayOpacity::~nsDisplayOpacity() {
MOZ_COUNT_DTOR(nsDisplayOpacity);
}
#endif
PRBool nsDisplayOpacity::IsOpaque(nsDisplayListBuilder* aBuilder) {
// We are never opaque, if our opacity was < 1 then we wouldn't have
// been created.
return PR_FALSE;
}
-void nsDisplayOpacity::Paint(nsDisplayListBuilder* aBuilder,
- nsIRenderingContext* aCtx)
-{
- float opacity = mFrame->GetStyleDisplay()->mOpacity;
-
- nsCOMPtr<nsIDeviceContext> devCtx;
- aCtx->GetDeviceContext(*getter_AddRefs(devCtx));
-
- gfxContext* ctx = aCtx->ThebesContext();
-
- ctx->Save();
+// nsDisplayOpacity uses layers for rendering
+already_AddRefed<Layer>
+nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager) {
+ nsRefPtr<Layer> layer =
+ mList.BuildLayer(aBuilder, aManager, &mChildLayers);
+ if (!layer)
+ return nsnull;
- ctx->NewPath();
- gfxRect r(mVisibleRect.x, mVisibleRect.y,
- mVisibleRect.width, mVisibleRect.height);
- r.ScaleInverse(devCtx->AppUnitsPerDevPixel());
- ctx->Rectangle(r, PR_TRUE);
- ctx->Clip();
+ layer->SetOpacity(mFrame->GetStyleDisplay()->mOpacity*layer->GetOpacity());
+ return layer.forget();
+}
- if (mNeedAlpha)
- ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
- else
- ctx->PushGroup(gfxASurface::CONTENT_COLOR);
-
- nsDisplayWrapList::Paint(aBuilder, aCtx);
-
- ctx->PopGroupToSource();
- ctx->SetOperator(gfxContext::OPERATOR_OVER);
- ctx->Paint(opacity);
-
- ctx->Restore();
+void
+nsDisplayOpacity::PaintThebesLayers(nsDisplayListBuilder* aBuilder) {
+ mList.PaintThebesLayers(aBuilder, mChildLayers);
}
PRBool nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
nsRegion* aVisibleRegionBeforeMove) {
NS_ASSERTION((aVisibleRegionBeforeMove != nsnull) == aBuilder->HasMovingFrames(),
"Should have aVisibleRegionBeforeMove when there are moving frames");
// Our children are translucent so we should not allow them to subtract
// area from aVisibleRegion. We do need to find out what is visible under
// our children in the temporary compositing buffer, because if our children
// paint our entire bounds opaquely then we don't need an alpha channel in
// the temporary compositing buffer.
- nsRegion visibleUnderChildren = *aVisibleRegion;
+ nsRect bounds = GetBounds(aBuilder);
+ nsRegion visibleUnderChildren;
+ visibleUnderChildren.And(*aVisibleRegion, bounds);
nsRegion visibleUnderChildrenBeforeMove;
if (aVisibleRegionBeforeMove) {
- visibleUnderChildrenBeforeMove = *aVisibleRegionBeforeMove;
+ visibleUnderChildrenBeforeMove.And(*aVisibleRegionBeforeMove, bounds);
}
- PRBool anyVisibleChildren =
+ return
nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren,
aVisibleRegionBeforeMove ? &visibleUnderChildrenBeforeMove : nsnull);
- if (!anyVisibleChildren)
- return PR_FALSE;
-
- // If we're analyzing moving content, then it doesn't really matter
- // what we set mNeedAlpha to, so let's conservatively set it to true so
- // we don't have to worry about getting it "correct" for the moving case.
- mNeedAlpha = aVisibleRegionBeforeMove ||
- visibleUnderChildren.Intersects(mVisibleRect);
- return PR_TRUE;
}
PRBool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
if (aItem->GetType() != TYPE_OPACITY)
return PR_FALSE;
// items for the same content element should be merged into a single
// compositing group
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
@@ -1185,20 +1576,17 @@ nsRect nsDisplayClip::GetBounds(nsDispla
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayClip::~nsDisplayClip() {
MOZ_COUNT_DTOR(nsDisplayClip);
}
#endif
void nsDisplayClip::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx) {
- aCtx->PushState();
- aCtx->SetClipRect(mClip, nsClipCombine_kIntersect);
- nsDisplayWrapList::Paint(aBuilder, aCtx);
- aCtx->PopState();
+ NS_ERROR("nsDisplayClip should have been flattened away for painting");
}
PRBool nsDisplayClip::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
nsRegion* aVisibleRegionBeforeMove) {
NS_ASSERTION((aVisibleRegionBeforeMove != nsnull) == aBuilder->HasMovingFrames(),
"Should have aVisibleRegionBeforeMove when there are moving frames");
@@ -1436,17 +1824,17 @@ void nsDisplayTransform::Paint(nsDisplay
/* Set the matrix for the transform based on the old matrix and the new
* transform data.
*/
gfx->SetMatrix(newTransformMatrix);
/* Now, send the paint call down.
*/
- mStoredList.Paint(aBuilder, aCtx);
+ mStoredList.GetList()->Paint(aBuilder, aCtx);
/* The AutoSaveRestore object will clean things up. */
}
PRBool nsDisplayTransform::ComputeVisibility(nsDisplayListBuilder *aBuilder,
nsRegion *aVisibleRegion,
nsRegion *aVisibleRegionBeforeMove)
{
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -47,16 +47,17 @@
#include "nsCOMPtr.h"
#include "nsIFrame.h"
#include "nsPoint.h"
#include "nsRect.h"
#include "nsISelection.h"
#include "nsCaret.h"
#include "plarena.h"
+#include "Layers.h"
#include <stdlib.h>
class nsIPresShell;
class nsIContent;
class nsRegion;
class nsIRenderingContext;
class nsIDeviceContext;
@@ -450,16 +451,19 @@ protected:
* subclass would just have a frame pointer, so its object would be just three
* pointers (vtable, next-item, frame).
*
* Display items belong to a list at all times (except temporarily as they
* move from one list to another).
*/
class nsDisplayItem : public nsDisplayItemLink {
public:
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layers::LayerManager LayerManager;
+
// This is never instantiated directly (it has pure virtual methods), so no
// need to count constructors and destructors.
nsDisplayItem(nsIFrame* aFrame) : mFrame(aFrame) {}
virtual ~nsDisplayItem() {}
void* operator new(size_t aSize,
nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
return aBuilder->Allocate(aSize);
@@ -543,16 +547,38 @@ public:
*/
virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder)
{ return PR_FALSE; }
/**
* Actually paint this item to some rendering context.
* Content outside mVisibleRect need not be painted.
*/
virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx) {}
+ /**
+ * Get the layer drawn by this display item, if any. If this display
+ * item doesn't have its own layer, then Paint will be called on it
+ * later. If it returns a layer here then Paint will not be called.
+ * This is called while aManager is in the construction phase.
+ * This is where content can decide to be rendered by the layer
+ * system (with the possibility of accelerated or off-main-thread
+ * rendering) instead of cairo.
+ *
+ * The caller (nsDisplayList) is responsible for setting the visible
+ * region of the layer.
+ */
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager)
+ { return nsnull; }
+ /**
+ * If BuildLayer returned non-null, then this method is called to
+ * paint any ThebesLayers which are descendants of the returned layer.
+ */
+ virtual void PaintThebesLayers(nsDisplayListBuilder* aBuilder)
+ {
+ }
/**
* On entry, aVisibleRegion contains the region (relative to ReferenceFrame())
* which may be visible. If the display item opaquely covers an area, it
* can remove that area from aVisibleRegion before returning.
* If we're doing scroll analysis with moving frames, then
* aVisibleRegionBeforeMove will be non-null and contains the region that
* would have been visible before the move. aVisibleRegion contains the
@@ -630,20 +656,26 @@ protected:
* slow so we don't support it. The methods that need to step downward
* (HitTest(), ComputeVisibility()) internally build a temporary array of all
* the items while they do the downward traversal, so overall they're still
* linear time. We have optimized for efficient AppendToTop() of both
* items and lists, with minimal codesize. AppendToBottom() is efficient too.
*/
class nsDisplayList {
public:
+ typedef mozilla::layers::Layer Layer;
+ typedef mozilla::layers::LayerManager LayerManager;
+ typedef mozilla::layers::ThebesLayer ThebesLayer;
+
/**
* Create an empty list.
*/
- nsDisplayList() {
+ nsDisplayList() :
+ mIsOpaque(PR_FALSE)
+ {
mTop = &mSentinel;
mSentinel.mAbove = nsnull;
#ifdef DEBUG
mDidComputeVisibility = PR_FALSE;
#endif
}
~nsDisplayList() {
if (mSentinel.mAbove) {
@@ -788,24 +820,33 @@ public:
/**
* Optimize the display list for visibility, removing any elements that
* are not visible. We put this logic here so it can be shared by top-level
* painting and also display items that maintain child lists.
* This is also a good place to put ComputeVisibility-related logic
* that must be applied to every display item. In particular, this
* sets mVisibleRect on each display item.
+ * This also sets mIsOpaque to whether aVisibleRegion is empty on return.
*
* @param aVisibleRegion the area that is visible, relative to the
* reference frame; on return, this contains the area visible under the list
*/
void ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
nsRegion* aVisibleRegionBeforeMove);
/**
+ * Returns true if the visible region output from ComputeVisiblity was
+ * empty, i.e. everything visible in this list is opaque.
+ */
+ PRBool IsOpaque() const {
+ NS_ASSERTION(mDidComputeVisibility, "Need to have called ComputeVisibility");
+ return mIsOpaque;
+ }
+ /**
* Paint the list to the rendering context. We assume that (0,0) in aCtx
* corresponds to the origin of the reference frame. For best results,
* aCtx's current transform should make (0,0) pixel-aligned. The
* rectangle in aDirtyRect is painted, which *must* be contained in the
* dirty rect used to construct the display list.
*
* ComputeVisibility must be called before Paint.
*/
@@ -816,30 +857,108 @@ public:
nsRect GetBounds(nsDisplayListBuilder* aBuilder) const;
/**
* Find the topmost display item that returns a non-null frame, and return
* the frame.
*/
nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
nsDisplayItem::HitTestState* aState) const;
+ /**
+ * This class represents a sublist of consecutive items in an nsDisplayList.
+ * The first item in the sublist is mStartItem and the last item
+ * is the item before mEndItem.
+ *
+ * These sublists are themselves organized into a linked list of all
+ * the ItemGroups associated with a given layer, via mNextItemsForLayer.
+ * This list will have more than one element if the display items in a layer
+ * come from different nsDisplayLists, or if they come from the same
+ * nsDisplayList but they aren't consecutive in that list.
+ *
+ * These objects are allocated from the nsDisplayListBuilder arena.
+ */
+ struct ItemGroup {
+ // If null, then the item group is empty.
+ nsDisplayItem* mStartItem;
+ nsDisplayItem* mEndItem;
+ ItemGroup* mNextItemsForLayer;
+ // The clipping (if any) that needs to be applied to all these items.
+ gfxRect mClipRect;
+ PRPackedBool mHasClipRect;
+
+ ItemGroup() : mStartItem(nsnull), mEndItem(nsnull),
+ mNextItemsForLayer(nsnull), mHasClipRect(PR_FALSE) {}
+
+ void* operator new(size_t aSize,
+ nsDisplayListBuilder* aBuilder) CPP_THROW_NEW {
+ return aBuilder->Allocate(aSize);
+ }
+ };
+ /**
+ * This class represents a layer and the display item(s) it
+ * will render. The items are stored in a linked list of ItemGroups.
+ */
+ struct LayerItems {
+ nsRefPtr<Layer> mLayer;
+ // equal to mLayer, or null if mLayer is not a ThebesLayer
+ ThebesLayer* mThebesLayer;
+ ItemGroup* mItems;
+ // The bounds of the visible region for this layer, in device pixels
+ nsIntRect mVisibleRect;
+
+ LayerItems(ItemGroup* aItems) :
+ mThebesLayer(nsnull), mItems(aItems)
+ {
+ }
+ };
+ /**
+ * Compute a list of layers needed to render this display list. The layers
+ * are added to aLayers, which must be empty on entry. This
+ * must be called while aManager is in the construction phase, because
+ * we construct layers belonging to aManager. The layers used to
+ * construct the layer tree (along with the display items associated
+ * with each layer) are returned in aLayers.
+ */
+ void BuildLayers(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsTArray<LayerItems>* aLayers) const;
+ /**
+ * Return a single layer which renders this display list. This
+ * must be called while aManager is in the construction phase, because
+ * we construct layers belonging to aManager. The layers used to
+ * construct the layer tree (along with the display items associated
+ * with each layer) are returned in aLayers.
+ */
+ already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager,
+ nsTArray<LayerItems>* aLayers) const;
+ /**
+ * Paint the ThebesLayers in the list of layers.
+ */
+ void PaintThebesLayers(nsDisplayListBuilder* aBuilder,
+ const nsTArray<LayerItems>& aLayers) const;
+
private:
// This class is only used on stack, so we don't have to worry about leaking
// it. Don't let us be heap-allocated!
void* operator new(size_t sz) CPP_THROW_NEW;
// Utility function used to massage the list during ComputeVisibility.
void FlattenTo(nsTArray<nsDisplayItem*>* aElements);
// Utility function used to massage the list during sorting, to rewrite
// any wrapper items with null GetUnderlyingFrame
void ExplodeAnonymousChildLists(nsDisplayListBuilder* aBuilder);
nsDisplayItemLink mSentinel;
nsDisplayItemLink* mTop;
+ // This is set to true by ComputeVisibility if the final visible region
+ // is empty (i.e. everything that was visible is covered by some
+ // opaque content in this list).
+ PRPackedBool mIsOpaque;
#ifdef DEBUG
PRPackedBool mDidComputeVisibility;
#endif
};
/**
* This is passed as a parameter to nsIFrame::BuildDisplayList. That method
* will put any generated items onto the appropriate list given here. It's
@@ -1379,30 +1498,27 @@ class nsDisplayOpacity : public nsDispla
public:
nsDisplayOpacity(nsIFrame* aFrame, nsDisplayList* aList);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayOpacity();
#endif
virtual Type GetType() { return TYPE_OPACITY; }
virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder);
- virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx);
+ virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+ LayerManager* aManager);
+ virtual void PaintThebesLayers(nsDisplayListBuilder* aBuilder);
virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
nsRegion* aVisibleRegionBeforeMove);
virtual PRBool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem);
NS_DISPLAY_DECL_NAME("Opacity")
private:
- /**
- * We set this to PR_FALSE if we can prove that our children cover our bounds
- * completely and opaquely, therefore no alpha channel is required in the
- * intermediate surface.
- */
- PRPackedBool mNeedAlpha;
+ nsTArray<nsDisplayList::LayerItems> mChildLayers;
};
/**
* nsDisplayClip can clip a list of items, but we take a single item
* initially and then later merge other items into it when we merge
* adjacent matching nsDisplayClips
*/
class nsDisplayClip : public nsDisplayWrapList {