michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "FrameLayerBuilder.h" michael@0: michael@0: #include "nsDisplayList.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "Layers.h" michael@0: #include "BasicLayers.h" michael@0: #include "gfxUtils.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "MaskLayerImageCache.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "LayerTreeInvalidation.h" michael@0: #include "nsSVGIntegrationUtils.h" michael@0: #include "ImageContainer.h" michael@0: #include "ActiveLayerTracker.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: #include "GeckoProfiler.h" michael@0: #include "mozilla/gfx/Tools.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "gfxPrefs.h" michael@0: michael@0: #include michael@0: michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::gfx; michael@0: michael@0: namespace mozilla { michael@0: michael@0: class ContainerState; michael@0: michael@0: FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey, michael@0: Layer* aLayer, LayerState aLayerState, uint32_t aGeneration) michael@0: michael@0: : mParent(aParent) michael@0: , mLayer(aLayer) michael@0: , mDisplayItemKey(aKey) michael@0: , mContainerLayerGeneration(aGeneration) michael@0: , mLayerState(aLayerState) michael@0: , mUsed(true) michael@0: , mIsInvalid(false) michael@0: { michael@0: } michael@0: michael@0: FrameLayerBuilder::DisplayItemData::DisplayItemData(DisplayItemData &toCopy) michael@0: { michael@0: // This isn't actually a copy-constructor; notice that it steals toCopy's michael@0: // mGeometry pointer. Be careful. michael@0: mParent = toCopy.mParent; michael@0: mLayer = toCopy.mLayer; michael@0: mInactiveManager = toCopy.mInactiveManager; michael@0: mFrameList = toCopy.mFrameList; michael@0: mGeometry = toCopy.mGeometry; michael@0: mDisplayItemKey = toCopy.mDisplayItemKey; michael@0: mClip = toCopy.mClip; michael@0: mContainerLayerGeneration = toCopy.mContainerLayerGeneration; michael@0: mLayerState = toCopy.mLayerState; michael@0: mUsed = toCopy.mUsed; michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame) michael@0: { michael@0: mFrameList.AppendElement(aFrame); michael@0: michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty())); michael@0: if (!array) { michael@0: array = new nsTArray(); michael@0: aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array); michael@0: } michael@0: array->AppendElement(this); michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame) michael@0: { michael@0: DebugOnly result = mFrameList.RemoveElement(aFrame); michael@0: NS_ASSERTION(result, "Can't remove a frame that wasn't added!"); michael@0: michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty())); michael@0: NS_ASSERTION(array, "Must be already stored on the frame!"); michael@0: array->RemoveElement(this); michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DisplayItemData::UpdateContents(Layer* aLayer, LayerState aState, michael@0: uint32_t aContainerLayerGeneration, michael@0: nsDisplayItem* aItem /* = nullptr */) michael@0: { michael@0: mLayer = aLayer; michael@0: mOptLayer = nullptr; michael@0: mInactiveManager = nullptr; michael@0: mLayerState = aState; michael@0: mContainerLayerGeneration = aContainerLayerGeneration; michael@0: mGeometry = nullptr; michael@0: mClip = DisplayItemClip(); michael@0: mUsed = true; michael@0: michael@0: if (!aItem) { michael@0: return; michael@0: } michael@0: michael@0: nsAutoTArray copy(mFrameList); michael@0: if (!copy.RemoveElement(aItem->Frame())) { michael@0: AddFrame(aItem->Frame()); michael@0: } michael@0: michael@0: nsAutoTArray mergedFrames; michael@0: aItem->GetMergedFrames(&mergedFrames); michael@0: for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { michael@0: if (!copy.RemoveElement(mergedFrames[i])) { michael@0: AddFrame(mergedFrames[i]); michael@0: } michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < copy.Length(); i++) { michael@0: RemoveFrame(copy[i]); michael@0: } michael@0: } michael@0: michael@0: static nsIFrame* sDestroyedFrame = nullptr; michael@0: FrameLayerBuilder::DisplayItemData::~DisplayItemData() michael@0: { michael@0: for (uint32_t i = 0; i < mFrameList.Length(); i++) { michael@0: nsIFrame* frame = mFrameList[i]; michael@0: if (frame == sDestroyedFrame) { michael@0: continue; michael@0: } michael@0: nsTArray *array = michael@0: reinterpret_cast*>(frame->Properties().Get(LayerManagerDataProperty())); michael@0: array->RemoveElement(this); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DisplayItemData::GetFrameListChanges(nsDisplayItem* aOther, michael@0: nsTArray& aOut) michael@0: { michael@0: aOut = mFrameList; michael@0: nsAutoTArray added; michael@0: if (!aOut.RemoveElement(aOther->Frame())) { michael@0: added.AppendElement(aOther->Frame()); michael@0: } michael@0: michael@0: nsAutoTArray mergedFrames; michael@0: aOther->GetMergedFrames(&mergedFrames); michael@0: for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { michael@0: if (!aOut.RemoveElement(mergedFrames[i])) { michael@0: added.AppendElement(mergedFrames[i]); michael@0: } michael@0: } michael@0: michael@0: aOut.AppendElements(added); michael@0: } michael@0: michael@0: /** michael@0: * This is the userdata we associate with a layer manager. michael@0: */ michael@0: class LayerManagerData : public LayerUserData { michael@0: public: michael@0: LayerManagerData(LayerManager *aManager) michael@0: : mLayerManager(aManager) michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: , mParent(nullptr) michael@0: #endif michael@0: , mInvalidateAllLayers(false) michael@0: { michael@0: MOZ_COUNT_CTOR(LayerManagerData); michael@0: } michael@0: ~LayerManagerData() { michael@0: MOZ_COUNT_DTOR(LayerManagerData); michael@0: } michael@0: michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: void Dump(const char *aPrefix = "") { michael@0: printf_stderr("%sLayerManagerData %p\n", aPrefix, this); michael@0: nsAutoCString prefix; michael@0: prefix += aPrefix; michael@0: prefix += " "; michael@0: mDisplayItems.EnumerateEntries( michael@0: FrameLayerBuilder::DumpDisplayItemDataForFrame, (void*)prefix.get()); michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Tracks which frames have layers associated with them. michael@0: */ michael@0: LayerManager *mLayerManager; michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: LayerManagerData *mParent; michael@0: #endif michael@0: nsTHashtable > mDisplayItems; michael@0: bool mInvalidateAllLayers; michael@0: }; michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame) michael@0: { michael@0: FrameProperties props = aFrame->Properties(); michael@0: props.Delete(LayerManagerDataProperty()); michael@0: } michael@0: michael@0: // a global cache of image containers used for mask layers michael@0: static MaskLayerImageCache* gMaskLayerImageCache = nullptr; michael@0: michael@0: static inline MaskLayerImageCache* GetMaskLayerImageCache() michael@0: { michael@0: if (!gMaskLayerImageCache) { michael@0: gMaskLayerImageCache = new MaskLayerImageCache(); michael@0: } michael@0: michael@0: return gMaskLayerImageCache; michael@0: } michael@0: michael@0: /** michael@0: * We keep a stack of these to represent the ThebesLayers that are michael@0: * currently available to have display items added to. michael@0: * We use a stack here because as much as possible we want to michael@0: * assign display items to existing ThebesLayers, and to the lowest michael@0: * ThebesLayer in z-order. This reduces the number of layers and michael@0: * makes it more likely a display item will be rendered to an opaque michael@0: * layer, giving us the best chance of getting subpixel AA. michael@0: */ michael@0: class ThebesLayerData { michael@0: public: michael@0: ThebesLayerData() : michael@0: mAnimatedGeometryRoot(nullptr), michael@0: mFixedPosFrameForLayerData(nullptr), michael@0: mReferenceFrame(nullptr), michael@0: mLayer(nullptr), michael@0: mIsSolidColorInVisibleRegion(false), michael@0: mSingleItemFixedToViewport(false), michael@0: mNeedComponentAlpha(false), michael@0: mForceTransparentSurface(false), michael@0: mImage(nullptr), michael@0: mCommonClipCount(-1), michael@0: mAllDrawingAbove(false) michael@0: {} michael@0: /** michael@0: * Record that an item has been added to the ThebesLayer, so we michael@0: * need to update our regions. michael@0: * @param aVisibleRect the area of the item that's visible michael@0: * @param aDrawRect the area of the item that would be drawn if it michael@0: * was completely visible michael@0: * @param aOpaqueRect if non-null, the area of the item that's opaque. michael@0: * We pass in a separate opaque rect because the opaque rect can be michael@0: * bigger than the visible rect, and we want to have the biggest michael@0: * opaque rect that we can. michael@0: * @param aSolidColor if non-null, the visible area of the item is michael@0: * a constant color given by *aSolidColor michael@0: */ michael@0: void Accumulate(ContainerState* aState, michael@0: nsDisplayItem* aItem, michael@0: const nsIntRect& aVisibleRect, michael@0: const nsIntRect& aDrawRect, michael@0: const DisplayItemClip& aClip); michael@0: const nsIFrame* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; } michael@0: michael@0: /** michael@0: * Add aHitRegion and aDispatchToContentHitRegion to the hit regions for michael@0: * this ThebesLayer. michael@0: */ michael@0: void AccumulateEventRegions(const nsIntRegion& aHitRegion, michael@0: const nsIntRegion& aMaybeHitRegion, michael@0: const nsIntRegion& aDispatchToContentHitRegion) michael@0: { michael@0: mHitRegion.Or(mHitRegion, aHitRegion); michael@0: mMaybeHitRegion.Or(mMaybeHitRegion, aMaybeHitRegion); michael@0: mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aDispatchToContentHitRegion); michael@0: } michael@0: michael@0: /** michael@0: * If this represents only a nsDisplayImage, and the image type michael@0: * supports being optimized to an ImageLayer (TYPE_RASTER only) returns michael@0: * an ImageContainer for the image. michael@0: */ michael@0: already_AddRefed CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder); michael@0: michael@0: void AddDrawAboveRegion(const nsIntRegion& aAbove) michael@0: { michael@0: if (!mAllDrawingAbove) { michael@0: mDrawAboveRegion.Or(mDrawAboveRegion, aAbove); michael@0: mDrawAboveRegion.SimplifyOutward(4); michael@0: } michael@0: } michael@0: michael@0: void AddVisibleAboveRegion(const nsIntRegion& aAbove) michael@0: { michael@0: if (!mAllDrawingAbove) { michael@0: mVisibleAboveRegion.Or(mVisibleAboveRegion, aAbove); michael@0: mVisibleAboveRegion.SimplifyOutward(4); michael@0: } michael@0: } michael@0: michael@0: void CopyAboveRegion(ThebesLayerData* aOther) michael@0: { michael@0: if (aOther->mAllDrawingAbove || mAllDrawingAbove) { michael@0: SetAllDrawingAbove(); michael@0: } else { michael@0: mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleAboveRegion); michael@0: mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleRegion); michael@0: mVisibleAboveRegion.SimplifyOutward(4); michael@0: mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawAboveRegion); michael@0: mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawRegion); michael@0: mDrawAboveRegion.SimplifyOutward(4); michael@0: } michael@0: } michael@0: michael@0: void SetAllDrawingAbove() michael@0: { michael@0: mAllDrawingAbove = true; michael@0: mDrawAboveRegion.SetEmpty(); michael@0: mVisibleAboveRegion.SetEmpty(); michael@0: } michael@0: michael@0: bool DrawAboveRegionIntersects(const nsIntRect& aRect) michael@0: { michael@0: return mAllDrawingAbove || mDrawAboveRegion.Intersects(aRect); michael@0: } michael@0: michael@0: bool DrawRegionIntersects(const nsIntRect& aRect) michael@0: { michael@0: return IsSubjectToAsyncTransforms() || mDrawRegion.Intersects(aRect); michael@0: } michael@0: michael@0: bool IntersectsVisibleAboveRegion(const nsIntRegion& aVisibleRegion) michael@0: { michael@0: if (mAllDrawingAbove) { michael@0: return true; michael@0: } michael@0: nsIntRegion visibleAboveIntersection; michael@0: visibleAboveIntersection.And(mVisibleAboveRegion, aVisibleRegion); michael@0: if (visibleAboveIntersection.IsEmpty()) { michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool IsSubjectToAsyncTransforms() michael@0: { michael@0: return mFixedPosFrameForLayerData != nullptr; michael@0: } michael@0: michael@0: /** michael@0: * The region of visible content in the layer, relative to the michael@0: * container layer (which is at the snapped top-left of the display michael@0: * list reference frame). michael@0: */ michael@0: nsIntRegion mVisibleRegion; michael@0: /** michael@0: * The region containing the bounds of all display items in the layer, michael@0: * regardless of visbility. michael@0: * Same coordinate system as mVisibleRegion. michael@0: * This is a conservative approximation: it contains the true region. michael@0: */ michael@0: nsIntRegion mDrawRegion; michael@0: /** michael@0: * The region of visible content in the layer that is opaque. michael@0: * Same coordinate system as mVisibleRegion. michael@0: */ michael@0: nsIntRegion mOpaqueRegion; michael@0: /** michael@0: * The definitely-hit region for this ThebesLayer. michael@0: */ michael@0: nsIntRegion mHitRegion; michael@0: /** michael@0: * The maybe-hit region for this ThebesLayer. michael@0: */ michael@0: nsIntRegion mMaybeHitRegion; michael@0: /** michael@0: * The dispatch-to-content hit region for this ThebesLayer. michael@0: */ michael@0: nsIntRegion mDispatchToContentHitRegion; michael@0: /** michael@0: * The "active scrolled root" for all content in the layer. Must michael@0: * be non-null; all content in a ThebesLayer must have the same michael@0: * active scrolled root. michael@0: */ michael@0: const nsIFrame* mAnimatedGeometryRoot; michael@0: /** michael@0: * If non-null, the frame from which we'll extract "fixed positioning" michael@0: * metadata for this layer. This can be a position:fixed frame or a viewport michael@0: * frame; the latter case is used for background-attachment:fixed content. michael@0: */ michael@0: const nsIFrame* mFixedPosFrameForLayerData; michael@0: const nsIFrame* mReferenceFrame; michael@0: ThebesLayer* mLayer; michael@0: /** michael@0: * If mIsSolidColorInVisibleRegion is true, this is the color of the visible michael@0: * region. michael@0: */ michael@0: nscolor mSolidColor; michael@0: /** michael@0: * True if every pixel in mVisibleRegion will have color mSolidColor. michael@0: */ michael@0: bool mIsSolidColorInVisibleRegion; michael@0: /** michael@0: * True if the layer contains exactly one item that returned true for michael@0: * ShouldFixToViewport. michael@0: */ michael@0: bool mSingleItemFixedToViewport; michael@0: /** michael@0: * True if there is any text visible in the layer that's over michael@0: * transparent pixels in the layer. michael@0: */ michael@0: bool mNeedComponentAlpha; michael@0: /** michael@0: * Set if the layer should be treated as transparent, even if its entire michael@0: * area is covered by opaque display items. For example, this needs to michael@0: * be set if something is going to "punch holes" in the layer by clearing michael@0: * part of its surface. michael@0: */ michael@0: bool mForceTransparentSurface; michael@0: michael@0: /** michael@0: * Stores the pointer to the nsDisplayImage if we want to michael@0: * convert this to an ImageLayer. michael@0: */ michael@0: nsDisplayImageContainer* mImage; michael@0: /** michael@0: * Stores the clip that we need to apply to the image or, if there is no michael@0: * image, a clip for SOME item in the layer. There is no guarantee which michael@0: * item's clip will be stored here and mItemClip should not be used to clip michael@0: * the whole layer - only some part of the clip should be used, as determined michael@0: * by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be michael@0: * no part at all. michael@0: */ michael@0: DisplayItemClip mItemClip; michael@0: /** michael@0: * The first mCommonClipCount rounded rectangle clips are identical for michael@0: * all items in the layer. michael@0: * -1 if there are no items in the layer; must be >=0 by the time that this michael@0: * data is popped from the stack. michael@0: */ michael@0: int32_t mCommonClipCount; michael@0: /* michael@0: * Updates mCommonClipCount by checking for rounded rect clips in common michael@0: * between the clip on a new item (aCurrentClip) and the common clips michael@0: * on items already in the layer (the first mCommonClipCount rounded rects michael@0: * in mItemClip). michael@0: */ michael@0: void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip); michael@0: michael@0: private: michael@0: /** michael@0: * The region of visible content above the layer and below the michael@0: * next ThebesLayerData currently in the stack, if any. Note that not michael@0: * all ThebesLayers for the container are in the ThebesLayerData stack. michael@0: * Same coordinate system as mVisibleRegion. michael@0: * This is a conservative approximation: it contains the true region. michael@0: */ michael@0: nsIntRegion mVisibleAboveRegion; michael@0: /** michael@0: * The region containing the bounds of all display items (regardless michael@0: * of visibility) in the layer and below the next ThebesLayerData michael@0: * currently in the stack, if any. michael@0: * Note that not all ThebesLayers for the container are in the michael@0: * ThebesLayerData stack. michael@0: * Same coordinate system as mVisibleRegion. michael@0: */ michael@0: nsIntRegion mDrawAboveRegion; michael@0: /** michael@0: * True if mDrawAboveRegion and mVisibleAboveRegion should be treated michael@0: * as infinite, and all display items should be considered 'above' this layer. michael@0: */ michael@0: bool mAllDrawingAbove; michael@0: }; michael@0: michael@0: /** michael@0: * This is a helper object used to build up the layer children for michael@0: * a ContainerLayer. michael@0: */ michael@0: class ContainerState { michael@0: public: michael@0: ContainerState(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: FrameLayerBuilder* aLayerBuilder, michael@0: nsIFrame* aContainerFrame, michael@0: nsDisplayItem* aContainerItem, michael@0: ContainerLayer* aContainerLayer, michael@0: const ContainerLayerParameters& aParameters) : michael@0: mBuilder(aBuilder), mManager(aManager), michael@0: mLayerBuilder(aLayerBuilder), michael@0: mContainerFrame(aContainerFrame), michael@0: mContainerLayer(aContainerLayer), michael@0: mParameters(aParameters), michael@0: mNextFreeRecycledThebesLayer(0) michael@0: { michael@0: nsPresContext* presContext = aContainerFrame->PresContext(); michael@0: mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); michael@0: mContainerReferenceFrame = aContainerItem ? aContainerItem->ReferenceFrameForChildren() : michael@0: mBuilder->FindReferenceFrameFor(mContainerFrame); michael@0: mContainerAnimatedGeometryRoot = aContainerItem michael@0: ? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder) michael@0: : mContainerReferenceFrame; michael@0: // When AllowResidualTranslation is false, display items will be drawn michael@0: // scaled with a translation by integer pixels, so we know how the snapping michael@0: // will work. michael@0: mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() && michael@0: !mParameters.AllowResidualTranslation(); michael@0: CollectOldLayers(); michael@0: } michael@0: michael@0: enum ProcessDisplayItemsFlags { michael@0: NO_COMPONENT_ALPHA = 0x01, michael@0: }; michael@0: michael@0: /** michael@0: * This is the method that actually walks a display list and builds michael@0: * the child layers. michael@0: */ michael@0: void ProcessDisplayItems(const nsDisplayList& aList, uint32_t aFlags); michael@0: /** michael@0: * This finalizes all the open ThebesLayers by popping every element off michael@0: * mThebesLayerDataStack, then sets the children of the container layer michael@0: * to be all the layers in mNewChildLayers in that order and removes any michael@0: * layers as children of the container that aren't in mNewChildLayers. michael@0: * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA, michael@0: * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA michael@0: */ michael@0: void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData); michael@0: michael@0: nsRect GetChildrenBounds() { return mBounds; } michael@0: michael@0: nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; } michael@0: michael@0: nsIntRect ScaleToNearestPixels(const nsRect& aRect) michael@0: { michael@0: return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion) michael@0: { michael@0: return aRegion.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap = false) michael@0: { michael@0: if (aSnap && mSnappingEnabled) { michael@0: return ScaleToNearestPixels(aRect); michael@0: } michael@0: return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap = false) michael@0: { michael@0: if (aSnap && mSnappingEnabled) { michael@0: return ScaleToNearestPixels(aRect); michael@0: } michael@0: return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: michael@0: nsIntRegion ScaleRegionToInsidePixels(const nsRegion& aRegion, bool aSnap = false) michael@0: { michael@0: if (aSnap && mSnappingEnabled) { michael@0: return ScaleRegionToNearestPixels(aRegion); michael@0: } michael@0: return aRegion.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: michael@0: nsIntRegion ScaleRegionToOutsidePixels(const nsRegion& aRegion, bool aSnap = false) michael@0: { michael@0: if (aSnap && mSnappingEnabled) { michael@0: return ScaleRegionToNearestPixels(aRegion); michael@0: } michael@0: return aRegion.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale, michael@0: mAppUnitsPerDevPixel); michael@0: } michael@0: michael@0: protected: michael@0: friend class ThebesLayerData; michael@0: michael@0: /** michael@0: * Grab the next recyclable ThebesLayer, or create one if there are no michael@0: * more recyclable ThebesLayers. Does any necessary invalidation of michael@0: * a recycled ThebesLayer, and sets up the transform on the ThebesLayer michael@0: * to account for scrolling. michael@0: */ michael@0: already_AddRefed CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot, michael@0: const nsIFrame *aReferenceFrame, michael@0: const nsPoint& aTopLeft); michael@0: /** michael@0: * Grab the next recyclable ColorLayer, or create one if there are no michael@0: * more recyclable ColorLayers. michael@0: */ michael@0: already_AddRefed CreateOrRecycleColorLayer(ThebesLayer* aThebes); michael@0: /** michael@0: * Grab the next recyclable ImageLayer, or create one if there are no michael@0: * more recyclable ImageLayers. michael@0: */ michael@0: already_AddRefed CreateOrRecycleImageLayer(ThebesLayer* aThebes); michael@0: /** michael@0: * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a michael@0: * mask layer which has been used for aLayer before), or create one if such michael@0: * a layer doesn't exist. michael@0: */ michael@0: already_AddRefed CreateOrRecycleMaskImageLayerFor(Layer* aLayer); michael@0: /** michael@0: * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them michael@0: * available for recycling. michael@0: */ michael@0: void CollectOldLayers(); michael@0: /** michael@0: * If aItem used to belong to a ThebesLayer, invalidates the area of michael@0: * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of michael@0: * aItem in that layer. michael@0: */ michael@0: void InvalidateForLayerChange(nsDisplayItem* aItem, michael@0: Layer* aNewLayer, michael@0: const DisplayItemClip& aClip, michael@0: const nsPoint& aTopLeft, michael@0: nsDisplayItemGeometry *aGeometry); michael@0: /** michael@0: * Try to determine whether the ThebesLayer at aThebesLayerIndex michael@0: * has a single opaque color behind it, over the entire bounds of its visible michael@0: * region. michael@0: * If successful, return that color, otherwise return NS_RGBA(0,0,0,0). michael@0: */ michael@0: nscolor FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex); michael@0: /** michael@0: * Find the fixed-pos frame, if any, containing (or equal to) michael@0: * aAnimatedGeometryRoot. Only return a fixed-pos frame if its viewport michael@0: * has a displayport. Updates *aVisibleRegion to be the intersection of michael@0: * aDrawRegion and the displayport, and updates *aIsSolidColorInVisibleRegion michael@0: * (if non-null) to false if the visible region grows. michael@0: * aDisplayItemFixedToViewport is true if the layer contains a single display michael@0: * item which returned true for ShouldFixToViewport. michael@0: * This can return the actual viewport frame for layers whose display items michael@0: * are directly on the viewport (e.g. background-attachment:fixed backgrounds). michael@0: */ michael@0: const nsIFrame* FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot, michael@0: bool aDisplayItemFixedToViewport); michael@0: void AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame, michael@0: const nsIntRegion& aDrawRegion, michael@0: nsIntRegion* aVisibleRegion, michael@0: bool* aIsSolidColorInVisibleRegion = nullptr); michael@0: /** michael@0: * Set fixed-pos layer metadata on aLayer according to the data for aFixedPosFrame. michael@0: */ michael@0: void SetFixedPositionLayerData(Layer* aLayer, michael@0: const nsIFrame* aFixedPosFrame); michael@0: /** michael@0: * Indicate that we are done adding items to the ThebesLayer at the top of michael@0: * mThebesLayerDataStack. Set the final visible region and opaque-content michael@0: * flag, and pop it off the stack. michael@0: */ michael@0: void PopThebesLayerData(); michael@0: /** michael@0: * Find the ThebesLayer to which we should assign the next display item. michael@0: * We scan the ThebesLayerData stack to find the topmost ThebesLayer michael@0: * that is compatible with the display item (i.e., has the same michael@0: * active scrolled root), and that has no content from other layers above michael@0: * it and intersecting the aVisibleRect. michael@0: * Returns the layer, and also updates the ThebesLayerData. Will michael@0: * push a new ThebesLayerData onto the stack if no suitable existing michael@0: * layer is found. If we choose a ThebesLayer that's already on the michael@0: * ThebesLayerData stack, later elements on the stack will be popped off. michael@0: * @param aVisibleRect the area of the next display item that's visible michael@0: * @param aAnimatedGeometryRoot the active scrolled root for the next michael@0: * display item michael@0: * @param aOpaqueRect if non-null, a region of the display item that is opaque michael@0: * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect michael@0: * will be painted with aSolidColor by the item michael@0: * @param aShouldFixToViewport if true, aAnimatedGeometryRoot is the viewport michael@0: * and we will be adding fixed-pos metadata for this layer because the michael@0: * display item returned true from ShouldFixToViewport. michael@0: */ michael@0: ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem, michael@0: const nsIntRect& aVisibleRect, michael@0: const nsIFrame* aAnimatedGeometryRoot, michael@0: const nsPoint& aTopLeft, michael@0: bool aShouldFixToViewport); michael@0: ThebesLayerData* GetTopThebesLayerData() michael@0: { michael@0: return mThebesLayerDataStack.IsEmpty() ? nullptr michael@0: : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get(); michael@0: } michael@0: michael@0: /* Build a mask layer to represent the clipping region. Will return null if michael@0: * there is no clipping specified or a mask layer cannot be built. michael@0: * Builds an ImageLayer for the appropriate backend; the mask is relative to michael@0: * aLayer's visible region. michael@0: * aLayer is the layer to be clipped. michael@0: * aRoundedRectClipCount is used when building mask layers for ThebesLayers, michael@0: * SetupMaskLayer will build a mask layer for only the first michael@0: * aRoundedRectClipCount rounded rects in aClip michael@0: */ michael@0: void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip, michael@0: uint32_t aRoundedRectClipCount = UINT32_MAX); michael@0: michael@0: bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList, michael@0: const nsIFrame **aAnimatedGeometryRoot); michael@0: michael@0: nsDisplayListBuilder* mBuilder; michael@0: LayerManager* mManager; michael@0: FrameLayerBuilder* mLayerBuilder; michael@0: nsIFrame* mContainerFrame; michael@0: const nsIFrame* mContainerReferenceFrame; michael@0: const nsIFrame* mContainerAnimatedGeometryRoot; michael@0: ContainerLayer* mContainerLayer; michael@0: ContainerLayerParameters mParameters; michael@0: /** michael@0: * The region of ThebesLayers that should be invalidated every time michael@0: * we recycle one. michael@0: */ michael@0: nsIntRegion mInvalidThebesContent; michael@0: nsRect mBounds; michael@0: nsAutoTArray,1> mThebesLayerDataStack; michael@0: /** michael@0: * We collect the list of children in here. During ProcessDisplayItems, michael@0: * the layers in this array either have mContainerLayer as their parent, michael@0: * or no parent. michael@0: */ michael@0: typedef nsAutoTArray,1> AutoLayersArray; michael@0: AutoLayersArray mNewChildLayers; michael@0: nsTArray > mRecycledThebesLayers; michael@0: nsDataHashtable, nsRefPtr > michael@0: mRecycledMaskImageLayers; michael@0: uint32_t mNextFreeRecycledThebesLayer; michael@0: nscoord mAppUnitsPerDevPixel; michael@0: bool mSnappingEnabled; michael@0: }; michael@0: michael@0: class ThebesDisplayItemLayerUserData : public LayerUserData michael@0: { michael@0: public: michael@0: ThebesDisplayItemLayerUserData() : michael@0: mMaskClipCount(0), michael@0: mForcedBackgroundColor(NS_RGBA(0,0,0,0)), michael@0: mXScale(1.f), mYScale(1.f), michael@0: mAppUnitsPerDevPixel(0), michael@0: mTranslation(0, 0), michael@0: mAnimatedGeometryRootPosition(0, 0) {} michael@0: michael@0: /** michael@0: * Record the number of clips in the Thebes layer's mask layer. michael@0: * Should not be reset when the layer is recycled since it is used to track michael@0: * changes in the use of mask layers. michael@0: */ michael@0: uint32_t mMaskClipCount; michael@0: michael@0: /** michael@0: * A color that should be painted over the bounds of the layer's visible michael@0: * region before any other content is painted. michael@0: */ michael@0: nscolor mForcedBackgroundColor; michael@0: michael@0: /** michael@0: * The resolution scale used. michael@0: */ michael@0: float mXScale, mYScale; michael@0: michael@0: /** michael@0: * The appunits per dev pixel for the items in this layer. michael@0: */ michael@0: nscoord mAppUnitsPerDevPixel; michael@0: michael@0: /** michael@0: * The offset from the ThebesLayer's 0,0 to the michael@0: * reference frame. This isn't necessarily the same as the transform michael@0: * set on the ThebesLayer since we might also be applying an extra michael@0: * offset specified by the parent ContainerLayer/ michael@0: */ michael@0: nsIntPoint mTranslation; michael@0: michael@0: /** michael@0: * We try to make 0,0 of the ThebesLayer be the top-left of the michael@0: * border-box of the "active scrolled root" frame (i.e. the nearest ancestor michael@0: * frame for the display items that is being actively scrolled). But michael@0: * we force the ThebesLayer transform to be an integer translation, and we may michael@0: * have a resolution scale, so we have to snap the ThebesLayer transform, so michael@0: * 0,0 may not be exactly the top-left of the active scrolled root. Here we michael@0: * store the coordinates in ThebesLayer space of the top-left of the michael@0: * active scrolled root. michael@0: */ michael@0: gfxPoint mAnimatedGeometryRootPosition; michael@0: michael@0: nsIntRegion mRegionToInvalidate; michael@0: michael@0: // The offset between the active scrolled root of this layer michael@0: // and the root of the container for the previous and current michael@0: // paints respectively. michael@0: nsPoint mLastAnimatedGeometryRootOrigin; michael@0: nsPoint mAnimatedGeometryRootOrigin; michael@0: michael@0: nsRefPtr mColorLayer; michael@0: nsRefPtr mImageLayer; michael@0: }; michael@0: michael@0: /* michael@0: * User data for layers which will be used as masks. michael@0: */ michael@0: struct MaskLayerUserData : public LayerUserData michael@0: { michael@0: MaskLayerUserData() michael@0: : mScaleX(-1.0f) michael@0: , mScaleY(-1.0f) michael@0: , mAppUnitsPerDevPixel(-1) michael@0: { } michael@0: michael@0: bool michael@0: operator== (const MaskLayerUserData& aOther) const michael@0: { michael@0: return mRoundedClipRects == aOther.mRoundedClipRects && michael@0: mScaleX == aOther.mScaleX && michael@0: mScaleY == aOther.mScaleY && michael@0: mOffset == aOther.mOffset && michael@0: mAppUnitsPerDevPixel == aOther.mAppUnitsPerDevPixel; michael@0: } michael@0: michael@0: nsRefPtr mImageKey; michael@0: // properties of the mask layer; the mask layer may be re-used if these michael@0: // remain unchanged. michael@0: nsTArray mRoundedClipRects; michael@0: // scale from the masked layer which is applied to the mask michael@0: float mScaleX, mScaleY; michael@0: // The ContainerLayerParameters offset which is applied to the mask's transform. michael@0: nsIntPoint mOffset; michael@0: int32_t mAppUnitsPerDevPixel; michael@0: }; michael@0: michael@0: /** michael@0: * The address of gThebesDisplayItemLayerUserData is used as the user michael@0: * data key for ThebesLayers created by FrameLayerBuilder. michael@0: * It identifies ThebesLayers used to draw non-layer content, which are michael@0: * therefore eligible for recycling. We want display items to be able to michael@0: * create their own dedicated ThebesLayers in BuildLayer, if necessary, michael@0: * and we wouldn't want to accidentally recycle those. michael@0: * The user data is a ThebesDisplayItemLayerUserData. michael@0: */ michael@0: uint8_t gThebesDisplayItemLayerUserData; michael@0: /** michael@0: * The address of gColorLayerUserData is used as the user michael@0: * data key for ColorLayers created by FrameLayerBuilder. michael@0: * The user data is null. michael@0: */ michael@0: uint8_t gColorLayerUserData; michael@0: /** michael@0: * The address of gImageLayerUserData is used as the user michael@0: * data key for ImageLayers created by FrameLayerBuilder. michael@0: * The user data is null. michael@0: */ michael@0: uint8_t gImageLayerUserData; michael@0: /** michael@0: * The address of gLayerManagerUserData is used as the user michael@0: * data key for retained LayerManagers managed by FrameLayerBuilder. michael@0: * The user data is a LayerManagerData. michael@0: */ michael@0: uint8_t gLayerManagerUserData; michael@0: /** michael@0: * The address of gMaskLayerUserData is used as the user michael@0: * data key for mask layers managed by FrameLayerBuilder. michael@0: * The user data is a MaskLayerUserData. michael@0: */ michael@0: uint8_t gMaskLayerUserData; michael@0: michael@0: /** michael@0: * Helper functions for getting user data and casting it to the correct type. michael@0: * aLayer is the layer where the user data is stored. michael@0: */ michael@0: MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer) michael@0: { michael@0: return static_cast(aLayer->GetUserData(&gMaskLayerUserData)); michael@0: } michael@0: michael@0: ThebesDisplayItemLayerUserData* GetThebesDisplayItemLayerUserData(Layer* aLayer) michael@0: { michael@0: return static_cast( michael@0: aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: } michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::Shutdown() michael@0: { michael@0: if (gMaskLayerImageCache) { michael@0: delete gMaskLayerImageCache; michael@0: gMaskLayerImageCache = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager, michael@0: ThebesLayerData* aLayerData) michael@0: { michael@0: mDisplayListBuilder = aBuilder; michael@0: mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext(); michael@0: if (mRootPresContext) { michael@0: mInitialDOMGeneration = mRootPresContext->GetDOMGeneration(); michael@0: } michael@0: mContainingThebesLayer = aLayerData; michael@0: aManager->SetUserData(&gLayerManagerLayerBuilder, this); michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::FlashPaint(gfxContext *aContext) michael@0: { michael@0: float r = float(rand()) / RAND_MAX; michael@0: float g = float(rand()) / RAND_MAX; michael@0: float b = float(rand()) / RAND_MAX; michael@0: aContext->SetColor(gfxRGBA(r, g, b, 0.4)); michael@0: aContext->Paint(); michael@0: } michael@0: michael@0: FrameLayerBuilder::DisplayItemData* michael@0: FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (array) { michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: DisplayItemData* item = array->ElementAt(i); michael@0: if (item->mDisplayItemKey == aKey && michael@0: item->mLayer->Manager() == mRetainingManager) { michael@0: return item; michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsACString& michael@0: AppendToString(nsACString& s, const nsIntRect& r, michael@0: const char* pfx="", const char* sfx="") michael@0: { michael@0: s += pfx; michael@0: s += nsPrintfCString( michael@0: "(x=%d, y=%d, w=%d, h=%d)", michael@0: r.x, r.y, r.width, r.height); michael@0: return s += sfx; michael@0: } michael@0: michael@0: nsACString& michael@0: AppendToString(nsACString& s, const nsIntRegion& r, michael@0: const char* pfx="", const char* sfx="") michael@0: { michael@0: s += pfx; michael@0: michael@0: nsIntRegionRectIterator it(r); michael@0: s += "< "; michael@0: while (const nsIntRect* sr = it.Next()) { michael@0: AppendToString(s, *sr) += "; "; michael@0: } michael@0: s += ">"; michael@0: michael@0: return s += sfx; michael@0: } michael@0: michael@0: /** michael@0: * Invalidate aRegion in aLayer. aLayer is in the coordinate system michael@0: * *after* aTranslation has been applied, so we need to michael@0: * apply the inverse of that transform before calling InvalidateRegion. michael@0: */ michael@0: static void michael@0: InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion, michael@0: const nsIntPoint& aTranslation) michael@0: { michael@0: // Convert the region from the coordinates of the container layer michael@0: // (relative to the snapped top-left of the display list reference frame) michael@0: // to the ThebesLayer's own coordinates michael@0: nsIntRegion rgn = aRegion; michael@0: rgn.MoveBy(-aTranslation); michael@0: aLayer->InvalidateRegion(rgn); michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: nsAutoCString str; michael@0: AppendToString(str, rgn); michael@0: printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get()); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: static void michael@0: InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect, michael@0: const DisplayItemClip& aClip, michael@0: const nsIntPoint& aTranslation) michael@0: { michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast(aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: michael@0: nsRect rect = aClip.ApplyNonRoundedIntersection(aRect); michael@0: michael@0: nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel); michael@0: InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation); michael@0: } michael@0: michael@0: michael@0: static nsIntPoint michael@0: GetTranslationForThebesLayer(ThebesLayer* aLayer) michael@0: { michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast michael@0: (aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: NS_ASSERTION(data, "Must be a tracked thebes layer!"); michael@0: michael@0: return data->mTranslation; michael@0: } michael@0: michael@0: /** michael@0: * Some frames can have multiple, nested, retaining layer managers michael@0: * associated with them (normal manager, inactive managers, SVG effects). michael@0: * In these cases we store the 'outermost' LayerManager data property michael@0: * on the frame since we can walk down the chain from there. michael@0: * michael@0: * If one of these frames has just been destroyed, we will free the inner michael@0: * layer manager when removing the entry from mFramesWithLayers. Destroying michael@0: * the layer manager destroys the LayerManagerData and calls into michael@0: * the DisplayItemData destructor. If the inner layer manager had any michael@0: * items with the same frame, then we attempt to retrieve properties michael@0: * from the deleted frame. michael@0: * michael@0: * Cache the destroyed frame pointer here so we can avoid crashing in this case. michael@0: */ michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame, michael@0: void* aPropertyValue) michael@0: { michael@0: sDestroyedFrame = aFrame; michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aPropertyValue); michael@0: michael@0: // Hold a reference to all the items so that they don't get michael@0: // deleted from under us. michael@0: nsTArray > arrayCopy; michael@0: for (uint32_t i = 0; i < array->Length(); ++i) { michael@0: arrayCopy.AppendElement(array->ElementAt(i)); michael@0: } michael@0: michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: if (array->Length()) { michael@0: LayerManagerData *rootData = array->ElementAt(0)->mParent; michael@0: while (rootData->mParent) { michael@0: rootData = rootData->mParent; michael@0: } michael@0: printf_stderr("Removing frame %p - dumping display data\n", aFrame); michael@0: rootData->Dump(); michael@0: } michael@0: #endif michael@0: michael@0: for (uint32_t i = 0; i < array->Length(); ++i) { michael@0: DisplayItemData* data = array->ElementAt(i); michael@0: michael@0: ThebesLayer* t = data->mLayer->AsThebesLayer(); michael@0: if (t) { michael@0: ThebesDisplayItemLayerUserData* thebesData = michael@0: static_cast(t->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: if (thebesData) { michael@0: nsRegion old = data->mGeometry->ComputeInvalidationRegion(); michael@0: nsIntRegion rgn = old.ScaleToOutsidePixels(thebesData->mXScale, thebesData->mYScale, thebesData->mAppUnitsPerDevPixel); michael@0: rgn.MoveBy(-GetTranslationForThebesLayer(t)); michael@0: thebesData->mRegionToInvalidate.Or(thebesData->mRegionToInvalidate, rgn); michael@0: thebesData->mRegionToInvalidate.SimplifyOutward(8); michael@0: } michael@0: } michael@0: michael@0: data->mParent->mDisplayItems.RemoveEntry(data); michael@0: } michael@0: michael@0: arrayCopy.Clear(); michael@0: delete array; michael@0: sDestroyedFrame = nullptr; michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager) michael@0: { michael@0: mRetainingManager = aManager; michael@0: LayerManagerData* data = static_cast michael@0: (aManager->GetUserData(&gLayerManagerUserData)); michael@0: if (data) { michael@0: mInvalidateAllLayers = data->mInvalidateAllLayers; michael@0: } else { michael@0: data = new LayerManagerData(aManager); michael@0: aManager->SetUserData(&gLayerManagerUserData, data); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer) michael@0: { michael@0: if (!mRetainingManager) { michael@0: return; michael@0: } michael@0: michael@0: DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager()); michael@0: NS_ASSERTION(data, "Must have already stored data for this item!"); michael@0: data->mOptLayer = aLayer; michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::DidEndTransaction() michael@0: { michael@0: GetMaskLayerImageCache()->Sweep(); michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::WillEndTransaction() michael@0: { michael@0: if (!mRetainingManager) { michael@0: return; michael@0: } michael@0: michael@0: // We need to save the data we'll need to support retaining. michael@0: LayerManagerData* data = static_cast michael@0: (mRetainingManager->GetUserData(&gLayerManagerUserData)); michael@0: NS_ASSERTION(data, "Must have data!"); michael@0: // Update all the frames that used to have layers. michael@0: data->mDisplayItems.EnumerateEntries(ProcessRemovedDisplayItems, this); michael@0: data->mInvalidateAllLayers = false; michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: FrameLayerBuilder::ProcessRemovedDisplayItems(nsRefPtrHashKey* aEntry, michael@0: void* aUserArg) michael@0: { michael@0: DisplayItemData* data = aEntry->GetKey(); michael@0: if (!data->mUsed) { michael@0: // This item was visible, but isn't anymore. michael@0: FrameLayerBuilder* layerBuilder = static_cast(aUserArg); michael@0: michael@0: ThebesLayer* t = data->mLayer->AsThebesLayer(); michael@0: if (t) { michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t); michael@0: } michael@0: #endif michael@0: InvalidatePostTransformRegion(t, michael@0: data->mGeometry->ComputeInvalidationRegion(), michael@0: data->mClip, michael@0: layerBuilder->GetLastPaintOffset(t)); michael@0: } michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: data->mUsed = false; michael@0: data->mIsInvalid = false; michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: FrameLayerBuilder::DumpDisplayItemDataForFrame(nsRefPtrHashKey* aEntry, michael@0: void* aClosure) michael@0: { michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: DisplayItemData *data = aEntry->GetKey(); michael@0: michael@0: nsAutoCString prefix; michael@0: prefix += static_cast(aClosure); michael@0: michael@0: const char *layerState; michael@0: switch (data->mLayerState) { michael@0: case LAYER_NONE: michael@0: layerState = "LAYER_NONE"; break; michael@0: case LAYER_INACTIVE: michael@0: layerState = "LAYER_INACTIVE"; break; michael@0: case LAYER_ACTIVE: michael@0: layerState = "LAYER_ACTIVE"; break; michael@0: case LAYER_ACTIVE_FORCE: michael@0: layerState = "LAYER_ACTIVE_FORCE"; break; michael@0: case LAYER_ACTIVE_EMPTY: michael@0: layerState = "LAYER_ACTIVE_EMPTY"; break; michael@0: case LAYER_SVG_EFFECTS: michael@0: layerState = "LAYER_SVG_EFFECTS"; break; michael@0: } michael@0: uint32_t mask = (1 << nsDisplayItem::TYPE_BITS) - 1; michael@0: michael@0: nsAutoCString str; michael@0: str += prefix; michael@0: str += nsPrintfCString("Frame %p ", data->mFrameList[0]); michael@0: str += nsDisplayItem::DisplayItemTypeName(static_cast(data->mDisplayItemKey & mask)); michael@0: if ((data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS)) { michael@0: str += nsPrintfCString("(%i)", data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS); michael@0: } michael@0: str += nsPrintfCString(", %s, Layer %p", layerState, data->mLayer.get()); michael@0: if (data->mOptLayer) { michael@0: str += nsPrintfCString(", OptLayer %p", data->mOptLayer.get()); michael@0: } michael@0: if (data->mInactiveManager) { michael@0: str += nsPrintfCString(", InactiveLayerManager %p", data->mInactiveManager.get()); michael@0: } michael@0: str += "\n"; michael@0: michael@0: printf_stderr("%s", str.get()); michael@0: michael@0: if (data->mInactiveManager) { michael@0: prefix += " "; michael@0: printf_stderr("%sDumping inactive layer info:\n", prefix.get()); michael@0: LayerManagerData* lmd = static_cast michael@0: (data->mInactiveManager->GetUserData(&gLayerManagerUserData)); michael@0: lmd->Dump(prefix.get()); michael@0: } michael@0: #endif michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* static */ FrameLayerBuilder::DisplayItemData* michael@0: FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem, michael@0: LayerManager* aManager) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aItem->Frame()->Properties().Get(LayerManagerDataProperty())); michael@0: if (array) { michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: DisplayItemData* item = array->ElementAt(i); michael@0: if (item->mDisplayItemKey == aItem->GetPerFrameKey() && michael@0: item->mLayer->Manager() == aManager) { michael@0: return item; michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (array) { michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: if (array->ElementAt(i)->mDisplayItemKey == aDisplayItemKey) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (!array) { michael@0: return; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: DisplayItemData* data = array->ElementAt(i); michael@0: if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) { michael@0: aCallback(aFrame, data); michael@0: } michael@0: } michael@0: } michael@0: michael@0: FrameLayerBuilder::DisplayItemData* michael@0: FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey) michael@0: { michael@0: // If we need to build a new layer tree, then just refuse to recycle michael@0: // anything. michael@0: if (!mRetainingManager || mInvalidateAllLayers) michael@0: return nullptr; michael@0: michael@0: DisplayItemData *data = GetDisplayItemData(aFrame, aDisplayItemKey); michael@0: michael@0: if (data && data->mLayer->Manager() == mRetainingManager) { michael@0: return data; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: Layer* michael@0: FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem, michael@0: nsDisplayItemGeometry** aOldGeometry, michael@0: DisplayItemClip** aOldClip, michael@0: nsTArray* aChangedFrames, michael@0: bool *aIsInvalid) michael@0: { michael@0: uint32_t key = aItem->GetPerFrameKey(); michael@0: nsIFrame* frame = aItem->Frame(); michael@0: michael@0: DisplayItemData* oldData = GetOldLayerForFrame(frame, key); michael@0: if (oldData) { michael@0: if (aOldGeometry) { michael@0: *aOldGeometry = oldData->mGeometry.get(); michael@0: } michael@0: if (aOldClip) { michael@0: *aOldClip = &oldData->mClip; michael@0: } michael@0: if (aChangedFrames) { michael@0: oldData->GetFrameListChanges(aItem, *aChangedFrames); michael@0: } michael@0: if (aIsInvalid) { michael@0: *aIsInvalid = oldData->mIsInvalid; michael@0: } michael@0: return oldData->mLayer; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: /* static */ Layer* michael@0: FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: michael@0: if (!array) { michael@0: return nullptr; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: DisplayItemData *data = array->ElementAt(i); michael@0: michael@0: if (data->mDisplayItemKey == aDisplayItemKey) { michael@0: return data->mLayer; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: ContainerState::CreateOrRecycleColorLayer(ThebesLayer *aThebes) michael@0: { michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast(aThebes->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: nsRefPtr layer = data->mColorLayer; michael@0: if (layer) { michael@0: layer->SetMaskLayer(nullptr); michael@0: } else { michael@0: // Create a new layer michael@0: layer = mManager->CreateColorLayer(); michael@0: if (!layer) michael@0: return nullptr; michael@0: // Mark this layer as being used for Thebes-painting display items michael@0: data->mColorLayer = layer; michael@0: layer->SetUserData(&gColorLayerUserData, nullptr); michael@0: michael@0: // Remove other layer types we might have stored for this ThebesLayer michael@0: data->mImageLayer = nullptr; michael@0: } michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ContainerState::CreateOrRecycleImageLayer(ThebesLayer *aThebes) michael@0: { michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast(aThebes->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: nsRefPtr layer = data->mImageLayer; michael@0: if (layer) { michael@0: layer->SetMaskLayer(nullptr); michael@0: } else { michael@0: // Create a new layer michael@0: layer = mManager->CreateImageLayer(); michael@0: if (!layer) michael@0: return nullptr; michael@0: // Mark this layer as being used for Thebes-painting display items michael@0: data->mImageLayer = layer; michael@0: layer->SetUserData(&gImageLayerUserData, nullptr); michael@0: michael@0: // Remove other layer types we might have stored for this ThebesLayer michael@0: data->mColorLayer = nullptr; michael@0: } michael@0: return layer.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer) michael@0: { michael@0: nsRefPtr result = mRecycledMaskImageLayers.Get(aLayer); michael@0: if (result) { michael@0: mRecycledMaskImageLayers.Remove(aLayer); michael@0: // XXX if we use clip on mask layers, null it out here michael@0: } else { michael@0: // Create a new layer michael@0: result = mManager->CreateImageLayer(); michael@0: if (!result) michael@0: return nullptr; michael@0: result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData()); michael@0: result->SetDisallowBigImage(true); michael@0: } michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: static const double SUBPIXEL_OFFSET_EPSILON = 0.02; michael@0: michael@0: /** michael@0: * This normally computes NSToIntRoundUp(aValue). However, if that would michael@0: * give a residual near 0.5 while aOldResidual is near -0.5, or michael@0: * it would give a residual near -0.5 while aOldResidual is near 0.5, then michael@0: * instead we return the integer in the other direction so that the residual michael@0: * is close to aOldResidual. michael@0: */ michael@0: static int32_t michael@0: RoundToMatchResidual(double aValue, double aOldResidual) michael@0: { michael@0: int32_t v = NSToIntRoundUp(aValue); michael@0: double residual = aValue - v; michael@0: if (aOldResidual < 0) { michael@0: if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) { michael@0: // Round up instead michael@0: return int32_t(ceil(aValue)); michael@0: } michael@0: } else if (aOldResidual > 0) { michael@0: if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) { michael@0: // Round down instead michael@0: return int32_t(floor(aValue)); michael@0: } michael@0: } michael@0: return v; michael@0: } michael@0: michael@0: static void michael@0: ResetScrollPositionForLayerPixelAlignment(const nsIFrame* aAnimatedGeometryRoot) michael@0: { michael@0: nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(aAnimatedGeometryRoot); michael@0: if (sf) { michael@0: sf->ResetScrollPositionForLayerPixelAlignment(); michael@0: } michael@0: } michael@0: michael@0: static void michael@0: InvalidateEntireThebesLayer(ThebesLayer* aLayer, const nsIFrame* aAnimatedGeometryRoot) michael@0: { michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Invalidating entire layer %p\n", aLayer); michael@0: } michael@0: #endif michael@0: nsIntRect invalidate = aLayer->GetValidRegion().GetBounds(); michael@0: aLayer->InvalidateRegion(invalidate); michael@0: aLayer->SetInvalidRectToVisibleRegion(); michael@0: ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot, michael@0: const nsIFrame* aReferenceFrame, michael@0: const nsPoint& aTopLeft) michael@0: { michael@0: // We need a new thebes layer michael@0: nsRefPtr layer; michael@0: ThebesDisplayItemLayerUserData* data; michael@0: #ifndef MOZ_ANDROID_OMTC michael@0: bool didResetScrollPositionForLayerPixelAlignment = false; michael@0: #endif michael@0: if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) { michael@0: // Recycle a layer michael@0: layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer]; michael@0: ++mNextFreeRecycledThebesLayer; michael@0: // Clear clip rect and mask layer so we don't accidentally stay clipped. michael@0: // We will reapply any necessary clipping. michael@0: layer->SetMaskLayer(nullptr); michael@0: michael@0: data = static_cast michael@0: (layer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: NS_ASSERTION(data, "Recycled ThebesLayers must have user data"); michael@0: michael@0: // This gets called on recycled ThebesLayers that are going to be in the michael@0: // final layer tree, so it's a convenient time to invalidate the michael@0: // content that changed where we don't know what ThebesLayer it belonged michael@0: // to, or if we need to invalidate the entire layer, we can do that. michael@0: // This needs to be done before we update the ThebesLayer to its new michael@0: // transform. See nsGfxScrollFrame::InvalidateInternal, where michael@0: // we ensure that mInvalidThebesContent is updated according to the michael@0: // scroll position as of the most recent paint. michael@0: if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) || michael@0: !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) || michael@0: data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) { michael@0: InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot); michael@0: #ifndef MOZ_ANDROID_OMTC michael@0: didResetScrollPositionForLayerPixelAlignment = true; michael@0: #endif michael@0: } michael@0: if (!data->mRegionToInvalidate.IsEmpty()) { michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get()); michael@0: } michael@0: #endif michael@0: layer->InvalidateRegion(data->mRegionToInvalidate); michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: nsAutoCString str; michael@0: AppendToString(str, data->mRegionToInvalidate); michael@0: printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get()); michael@0: } michael@0: #endif michael@0: data->mRegionToInvalidate.SetEmpty(); michael@0: } michael@0: michael@0: // We do not need to Invalidate these areas in the widget because we michael@0: // assume the caller of InvalidateThebesLayerContents has ensured michael@0: // the area is invalidated in the widget. michael@0: } else { michael@0: // Check whether the layer will be scrollable. This is used as a hint to michael@0: // influence whether tiled layers are used or not. michael@0: bool canScroll = false; michael@0: nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent(); michael@0: if (animatedGeometryRootParent && michael@0: animatedGeometryRootParent->GetType() == nsGkAtoms::scrollFrame) { michael@0: canScroll = true; michael@0: } michael@0: // Create a new thebes layer michael@0: layer = mManager->CreateThebesLayerWithHint(canScroll ? LayerManager::SCROLLABLE : michael@0: LayerManager::NONE); michael@0: if (!layer) michael@0: return nullptr; michael@0: // Mark this layer as being used for Thebes-painting display items michael@0: data = new ThebesDisplayItemLayerUserData(); michael@0: layer->SetUserData(&gThebesDisplayItemLayerUserData, data); michael@0: ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot); michael@0: #ifndef MOZ_ANDROID_OMTC michael@0: didResetScrollPositionForLayerPixelAlignment = true; michael@0: #endif michael@0: } michael@0: data->mXScale = mParameters.mXScale; michael@0: data->mYScale = mParameters.mYScale; michael@0: data->mLastAnimatedGeometryRootOrigin = data->mAnimatedGeometryRootOrigin; michael@0: data->mAnimatedGeometryRootOrigin = aTopLeft; michael@0: data->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel; michael@0: layer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation()); michael@0: michael@0: mLayerBuilder->SaveLastPaintOffset(layer); michael@0: michael@0: // Set up transform so that 0,0 in the Thebes layer corresponds to the michael@0: // (pixel-snapped) top-left of the aAnimatedGeometryRoot. michael@0: nsPoint offset = aAnimatedGeometryRoot->GetOffsetToCrossDoc(aReferenceFrame); michael@0: nscoord appUnitsPerDevPixel = aAnimatedGeometryRoot->PresContext()->AppUnitsPerDevPixel(); michael@0: gfxPoint scaledOffset( michael@0: NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale, michael@0: NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale); michael@0: // We call RoundToMatchResidual here so that the residual after rounding michael@0: // is close to data->mAnimatedGeometryRootPosition if possible. michael@0: nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, data->mAnimatedGeometryRootPosition.x), michael@0: RoundToMatchResidual(scaledOffset.y, data->mAnimatedGeometryRootPosition.y)); michael@0: data->mTranslation = pixOffset; michael@0: pixOffset += mParameters.mOffset; michael@0: Matrix matrix; michael@0: matrix.Translate(pixOffset.x, pixOffset.y); michael@0: layer->SetBaseTransform(Matrix4x4::From2D(matrix)); michael@0: michael@0: // FIXME: Temporary workaround for bug 681192 and bug 724786. michael@0: #ifndef MOZ_ANDROID_OMTC michael@0: // Calculate exact position of the top-left of the active scrolled root. michael@0: // This might not be 0,0 due to the snapping in ScaleToNearestPixels. michael@0: gfxPoint animatedGeometryRootTopLeft = scaledOffset - ThebesPoint(matrix.GetTranslation()) + mParameters.mOffset; michael@0: // If it has changed, then we need to invalidate the entire layer since the michael@0: // pixels in the layer buffer have the content at a (subpixel) offset michael@0: // from what we need. michael@0: if (!animatedGeometryRootTopLeft.WithinEpsilonOf(data->mAnimatedGeometryRootPosition, SUBPIXEL_OFFSET_EPSILON)) { michael@0: data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft; michael@0: InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot); michael@0: } else if (didResetScrollPositionForLayerPixelAlignment) { michael@0: data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft; michael@0: } michael@0: #endif michael@0: michael@0: return layer.forget(); michael@0: } michael@0: michael@0: #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING) michael@0: /** michael@0: * Returns the appunits per dev pixel for the item's frame michael@0: */ michael@0: static int32_t michael@0: AppUnitsPerDevPixel(nsDisplayItem* aItem) michael@0: { michael@0: // The underlying frame for zoom items is the root frame of the subdocument. michael@0: // But zoom display items report their bounds etc using the parent document's michael@0: // APD because zoom items act as a conversion layer between the two different michael@0: // APDs. michael@0: if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) { michael@0: return static_cast(aItem)->GetParentAppUnitsPerDevPixel(); michael@0: } michael@0: return aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); michael@0: } michael@0: #endif michael@0: michael@0: /** michael@0: * Restrict the visible region of aLayer to the region that is actually visible. michael@0: * Because we only reduce the visible region here, we don't need to worry michael@0: * about whether CONTENT_OPAQUE is set; if layer was opaque in the old michael@0: * visible region, it will still be opaque in the new one. michael@0: * @param aLayerVisibleRegion the visible region of the layer, in the layer's michael@0: * coordinate space michael@0: * @param aRestrictToRect the rect to restrict the visible region to, in the michael@0: * parent's coordinate system michael@0: */ michael@0: static void michael@0: SetVisibleRegionForLayer(Layer* aLayer, const nsIntRegion& aLayerVisibleRegion, michael@0: const nsIntRect& aRestrictToRect) michael@0: { michael@0: gfx3DMatrix transform; michael@0: To3DMatrix(aLayer->GetTransform(), transform); michael@0: michael@0: // if 'transform' is not invertible, then nothing will be displayed michael@0: // for the layer, so it doesn't really matter what we do here michael@0: gfxRect itemVisible(aRestrictToRect.x, aRestrictToRect.y, michael@0: aRestrictToRect.width, aRestrictToRect.height); michael@0: nsIntRect childBounds = aLayerVisibleRegion.GetBounds(); michael@0: gfxRect childGfxBounds(childBounds.x, childBounds.y, michael@0: childBounds.width, childBounds.height); michael@0: gfxRect layerVisible = transform.UntransformBounds(itemVisible, childGfxBounds); michael@0: layerVisible.RoundOut(); michael@0: michael@0: nsIntRect visibleRect; michael@0: if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect)) { michael@0: aLayer->SetVisibleRegion(nsIntRegion()); michael@0: } else { michael@0: nsIntRegion rgn; michael@0: rgn.And(aLayerVisibleRegion, visibleRect); michael@0: aLayer->SetVisibleRegion(rgn); michael@0: } michael@0: } michael@0: michael@0: nscolor michael@0: ContainerState::FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex) michael@0: { michael@0: ThebesLayerData* target = mThebesLayerDataStack[aThebesLayerIndex]; michael@0: for (int32_t i = aThebesLayerIndex - 1; i >= 0; --i) { michael@0: ThebesLayerData* candidate = mThebesLayerDataStack[i]; michael@0: if (candidate->IntersectsVisibleAboveRegion(target->mVisibleRegion)) { michael@0: // Some non-Thebes content between target and candidate; this is michael@0: // hopeless michael@0: break; michael@0: } michael@0: michael@0: nsIntRegion intersection; michael@0: intersection.And(candidate->mVisibleRegion, target->mVisibleRegion); michael@0: if (intersection.IsEmpty()) { michael@0: // The layer doesn't intersect our target, ignore it and move on michael@0: continue; michael@0: } michael@0: michael@0: // The candidate intersects our target. If any layer has a solid-color michael@0: // area behind our target, this must be it. Scan its display items. michael@0: nsIntRect deviceRect = target->mVisibleRegion.GetBounds(); michael@0: nsRect appUnitRect = deviceRect.ToAppUnits(mAppUnitsPerDevPixel); michael@0: appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale); michael@0: michael@0: FrameLayerBuilder::ThebesLayerItemsEntry* entry = michael@0: mLayerBuilder->GetThebesLayerItemsEntry(candidate->mLayer); michael@0: NS_ASSERTION(entry, "Must know about this layer!"); michael@0: for (int32_t j = entry->mItems.Length() - 1; j >= 0; --j) { michael@0: nsDisplayItem* item = entry->mItems[j].mItem; michael@0: bool snap; michael@0: nsRect bounds = item->GetBounds(mBuilder, &snap); michael@0: if (snap && mSnappingEnabled) { michael@0: nsIntRect snappedBounds = ScaleToNearestPixels(bounds); michael@0: if (!snappedBounds.Intersects(deviceRect)) michael@0: continue; michael@0: michael@0: if (!snappedBounds.Contains(deviceRect)) michael@0: break; michael@0: michael@0: } else { michael@0: // The layer's visible rect is already (close enough to) pixel michael@0: // aligned, so no need to round out and in here. michael@0: if (!bounds.Intersects(appUnitRect)) michael@0: continue; michael@0: michael@0: if (!bounds.Contains(appUnitRect)) michael@0: break; michael@0: } michael@0: michael@0: nscolor color; michael@0: if (item->IsUniform(mBuilder, &color) && NS_GET_A(color) == 255) michael@0: return color; michael@0: michael@0: break; michael@0: } michael@0: break; michael@0: } michael@0: return NS_RGBA(0,0,0,0); michael@0: } michael@0: michael@0: void michael@0: ThebesLayerData::UpdateCommonClipCount( michael@0: const DisplayItemClip& aCurrentClip) michael@0: { michael@0: if (mCommonClipCount >= 0) { michael@0: mCommonClipCount = mItemClip.GetCommonRoundedRectCount(aCurrentClip, mCommonClipCount); michael@0: } else { michael@0: // first item in the layer michael@0: mCommonClipCount = aCurrentClip.GetRoundedRectCount(); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: ThebesLayerData::CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: if (!mImage) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mImage->GetContainer(mLayer->Manager(), aBuilder); michael@0: } michael@0: michael@0: const nsIFrame* michael@0: ContainerState::FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot, michael@0: bool aDisplayItemFixedToViewport) michael@0: { michael@0: if (!mManager->IsWidgetLayerManager()) { michael@0: // Never attach any fixed-pos metadata to inactive layers, it's pointless! michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPresContext* presContext = mContainerFrame->PresContext(); michael@0: nsIFrame* viewport = presContext->PresShell()->GetRootFrame(); michael@0: michael@0: if (viewport == aAnimatedGeometryRoot && aDisplayItemFixedToViewport && michael@0: nsLayoutUtils::ViewportHasDisplayPort(presContext)) { michael@0: // Probably a background-attachment:fixed item michael@0: return viewport; michael@0: } michael@0: // Viewports with no fixed-pos frames are not relevant. michael@0: if (!viewport->GetFirstChild(nsIFrame::kFixedList)) { michael@0: return nullptr; michael@0: } michael@0: for (const nsIFrame* f = aAnimatedGeometryRoot; f; f = f->GetParent()) { michael@0: if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) { michael@0: return f; michael@0: } michael@0: if (f == mContainerReferenceFrame) { michael@0: // The metadata will go on an ancestor layer if necessary. michael@0: return nullptr; michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: ContainerState::AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame, michael@0: const nsIntRegion& aDrawRegion, michael@0: nsIntRegion* aVisibleRegion, michael@0: bool* aIsSolidColorInVisibleRegion) michael@0: { michael@0: if (!aFixedPosFrame) { michael@0: return; michael@0: } michael@0: michael@0: nsRect fixedVisibleRect; michael@0: nsPresContext* presContext = aFixedPosFrame->PresContext(); michael@0: nsIPresShell* presShell = presContext->PresShell(); michael@0: DebugOnly hasDisplayPort = michael@0: nsLayoutUtils::ViewportHasDisplayPort(presContext, &fixedVisibleRect); michael@0: NS_ASSERTION(hasDisplayPort, "No fixed-pos layer data if there's no displayport"); michael@0: // Display ports are relative to the viewport, convert it to be relative michael@0: // to our reference frame. michael@0: nsIFrame* viewport = presShell->GetRootFrame(); michael@0: if (aFixedPosFrame != viewport) { michael@0: // position: fixed items are reflowed into and only drawn inside the michael@0: // viewport, or the scroll position clamping scrollport size, if one is michael@0: // set. We differentiate background-attachment: fixed items from michael@0: // position: fixed items by the fact that background-attachment: fixed michael@0: // items use the viewport as their aFixedPosFrame. michael@0: NS_ASSERTION(aFixedPosFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED, michael@0: "should be position fixed items only"); michael@0: fixedVisibleRect.MoveTo(0, 0); michael@0: if (presShell->IsScrollPositionClampingScrollPortSizeSet()) { michael@0: fixedVisibleRect.SizeTo(presShell->GetScrollPositionClampingScrollPortSize()); michael@0: } else { michael@0: fixedVisibleRect.SizeTo(viewport->GetSize()); michael@0: } michael@0: } michael@0: fixedVisibleRect += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame); michael@0: nsIntRegion newVisibleRegion; michael@0: newVisibleRegion.And(ScaleToOutsidePixels(fixedVisibleRect, false), michael@0: aDrawRegion); michael@0: if (!aVisibleRegion->Contains(newVisibleRegion)) { michael@0: if (aIsSolidColorInVisibleRegion) { michael@0: *aIsSolidColorInVisibleRegion = false; michael@0: } michael@0: *aVisibleRegion = newVisibleRegion; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerState::SetFixedPositionLayerData(Layer* aLayer, michael@0: const nsIFrame* aFixedPosFrame) michael@0: { michael@0: aLayer->SetIsFixedPosition(aFixedPosFrame != nullptr); michael@0: if (!aFixedPosFrame) { michael@0: return; michael@0: } michael@0: michael@0: nsPresContext* presContext = aFixedPosFrame->PresContext(); michael@0: michael@0: const nsIFrame* viewportFrame = aFixedPosFrame->GetParent(); michael@0: // anchorRect will be in the container's coordinate system (aLayer's parent layer). michael@0: // This is the same as the display items' reference frame. michael@0: nsRect anchorRect; michael@0: if (viewportFrame) { michael@0: // Fixed position frames are reflowed into the scroll-port size if one has michael@0: // been set. michael@0: if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) { michael@0: anchorRect.SizeTo(presContext->PresShell()->GetScrollPositionClampingScrollPortSize()); michael@0: } else { michael@0: anchorRect.SizeTo(viewportFrame->GetSize()); michael@0: } michael@0: } else { michael@0: // A display item directly attached to the viewport. michael@0: // For background-attachment:fixed items, the anchor point is always the michael@0: // top-left of the viewport currently. michael@0: viewportFrame = aFixedPosFrame; michael@0: } michael@0: // The anchorRect top-left is always the viewport top-left. michael@0: anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(mContainerReferenceFrame)); michael@0: michael@0: nsLayoutUtils::SetFixedPositionLayerData(aLayer, michael@0: viewportFrame, anchorRect, aFixedPosFrame, presContext, mParameters); michael@0: } michael@0: michael@0: static gfx3DMatrix michael@0: GetTransformToRoot(Layer* aLayer) michael@0: { michael@0: Matrix4x4 transform = aLayer->GetTransform(); michael@0: for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) { michael@0: transform = transform * l->GetTransform(); michael@0: } michael@0: gfx3DMatrix result; michael@0: To3DMatrix(transform, result); michael@0: return result; michael@0: } michael@0: michael@0: static void michael@0: AddTransformedBoundsToRegion(const nsIntRegion& aRegion, michael@0: const gfx3DMatrix& aTransform, michael@0: nsIntRegion* aDest) michael@0: { michael@0: nsIntRect bounds = aRegion.GetBounds(); michael@0: gfxRect transformed = michael@0: aTransform.TransformBounds(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); michael@0: transformed.RoundOut(); michael@0: nsIntRect intRect; michael@0: if (!gfxUtils::GfxRectToIntRect(transformed, &intRect)) { michael@0: // This should only fail if coordinates are too big to fit in an int32 michael@0: *aDest = nsIntRect(-INT32_MAX/2, -INT32_MAX/2, INT32_MAX, INT32_MAX); michael@0: return; michael@0: } michael@0: aDest->Or(*aDest, intRect); michael@0: } michael@0: michael@0: static bool michael@0: CanOptimizeAwayThebesLayer(ThebesLayerData* aData, michael@0: FrameLayerBuilder* aLayerBuilder) michael@0: { michael@0: bool isRetained = aData->mLayer->Manager()->IsWidgetLayerManager(); michael@0: if (!isRetained) { michael@0: return false; michael@0: } michael@0: michael@0: // If there's no thebes layer with valid content in it that we can reuse, michael@0: // always create a color or image layer (and potentially throw away an michael@0: // existing completely invalid thebes layer). michael@0: if (aData->mLayer->GetValidRegion().IsEmpty()) { michael@0: return true; michael@0: } michael@0: michael@0: // There is an existing thebes layer we can reuse. Throwing it away can make michael@0: // compositing cheaper (see bug 946952), but it might cause us to re-allocate michael@0: // the thebes layer frequently due to an animation. So we only discard it if michael@0: // we're in tree compression mode, which is triggered at a low frequency. michael@0: return aLayerBuilder->CheckInLayerTreeCompressionMode(); michael@0: } michael@0: michael@0: void michael@0: ContainerState::PopThebesLayerData() michael@0: { michael@0: NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop"); michael@0: michael@0: int32_t lastIndex = mThebesLayerDataStack.Length() - 1; michael@0: ThebesLayerData* data = mThebesLayerDataStack[lastIndex]; michael@0: michael@0: AdjustLayerDataForFixedPositioning(data->mFixedPosFrameForLayerData, michael@0: data->mDrawRegion, michael@0: &data->mVisibleRegion, michael@0: &data->mIsSolidColorInVisibleRegion); michael@0: nsRefPtr layer; michael@0: nsRefPtr imageContainer = data->CanOptimizeImageLayer(mBuilder); michael@0: michael@0: if ((data->mIsSolidColorInVisibleRegion || imageContainer) && michael@0: CanOptimizeAwayThebesLayer(data, mLayerBuilder)) { michael@0: NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer), michael@0: "Can't be a solid color as well as an image!"); michael@0: if (imageContainer) { michael@0: nsRefPtr imageLayer = CreateOrRecycleImageLayer(data->mLayer); michael@0: imageLayer->SetContainer(imageContainer); michael@0: data->mImage->ConfigureLayer(imageLayer, mParameters.mOffset); michael@0: imageLayer->SetPostScale(mParameters.mXScale, michael@0: mParameters.mYScale); michael@0: if (data->mItemClip.HasClip()) { michael@0: nsIntRect clip = ScaleToNearestPixels(data->mItemClip.GetClipRect()); michael@0: clip.MoveBy(mParameters.mOffset); michael@0: imageLayer->SetClipRect(&clip); michael@0: } else { michael@0: imageLayer->SetClipRect(nullptr); michael@0: } michael@0: layer = imageLayer; michael@0: mLayerBuilder->StoreOptimizedLayerForFrame(data->mImage, michael@0: imageLayer); michael@0: } else { michael@0: nsRefPtr colorLayer = CreateOrRecycleColorLayer(data->mLayer); michael@0: colorLayer->SetColor(data->mSolidColor); michael@0: michael@0: // Copy transform michael@0: colorLayer->SetBaseTransform(data->mLayer->GetBaseTransform()); michael@0: colorLayer->SetPostScale(data->mLayer->GetPostXScale(), data->mLayer->GetPostYScale()); michael@0: michael@0: nsIntRect visibleRect = data->mVisibleRegion.GetBounds(); michael@0: visibleRect.MoveBy(-GetTranslationForThebesLayer(data->mLayer)); michael@0: colorLayer->SetBounds(visibleRect); michael@0: michael@0: layer = colorLayer; michael@0: } michael@0: michael@0: NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???"); michael@0: AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer); michael@0: NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?"); michael@0: mNewChildLayers.InsertElementAt(index + 1, layer); michael@0: michael@0: // Hide the ThebesLayer. We leave it in the layer tree so that we michael@0: // can find and recycle it later. michael@0: nsIntRect emptyRect; michael@0: data->mLayer->SetClipRect(&emptyRect); michael@0: data->mLayer->SetVisibleRegion(nsIntRegion()); michael@0: data->mLayer->SetEventRegions(EventRegions()); michael@0: } else { michael@0: layer = data->mLayer; michael@0: imageContainer = nullptr; michael@0: layer->SetClipRect(nullptr); michael@0: } michael@0: michael@0: Matrix transform; michael@0: if (!layer->GetTransform().Is2D(&transform)) { michael@0: NS_ERROR("Only 2D transformations currently supported"); michael@0: } michael@0: michael@0: // ImageLayers are already configured with a visible region michael@0: if (!imageContainer) { michael@0: NS_ASSERTION(!transform.HasNonIntegerTranslation(), michael@0: "Matrix not just an integer translation?"); michael@0: // Convert from relative to the container to relative to the michael@0: // ThebesLayer itself. michael@0: nsIntRegion rgn = data->mVisibleRegion; michael@0: rgn.MoveBy(-GetTranslationForThebesLayer(data->mLayer)); michael@0: layer->SetVisibleRegion(rgn); michael@0: } michael@0: michael@0: nsIntRegion transparentRegion; michael@0: transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion); michael@0: bool isOpaque = transparentRegion.IsEmpty(); michael@0: // For translucent ThebesLayers, try to find an opaque background michael@0: // color that covers the entire area beneath it so we can pull that michael@0: // color into this layer to make it opaque. michael@0: if (layer == data->mLayer) { michael@0: nscolor backgroundColor = NS_RGBA(0,0,0,0); michael@0: if (!isOpaque) { michael@0: backgroundColor = FindOpaqueBackgroundColorFor(lastIndex); michael@0: if (NS_GET_A(backgroundColor) == 255) { michael@0: isOpaque = true; michael@0: } michael@0: } michael@0: michael@0: // Store the background color michael@0: ThebesDisplayItemLayerUserData* userData = michael@0: GetThebesDisplayItemLayerUserData(data->mLayer); michael@0: NS_ASSERTION(userData, "where did our user data go?"); michael@0: if (userData->mForcedBackgroundColor != backgroundColor) { michael@0: // Invalidate the entire target ThebesLayer since we're changing michael@0: // the background color michael@0: data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion()); michael@0: } michael@0: userData->mForcedBackgroundColor = backgroundColor; michael@0: michael@0: // use a mask layer for rounded rect clipping. michael@0: // data->mCommonClipCount may be -1 if we haven't put any actual michael@0: // drawable items in this layer (i.e. it's only catching events). michael@0: int32_t commonClipCount = std::max(0, data->mCommonClipCount); michael@0: SetupMaskLayer(layer, data->mItemClip, commonClipCount); michael@0: // copy commonClipCount to the entry michael@0: FrameLayerBuilder::ThebesLayerItemsEntry* entry = mLayerBuilder-> michael@0: GetThebesLayerItemsEntry(static_cast(layer.get())); michael@0: entry->mCommonClipCount = commonClipCount; michael@0: } else { michael@0: // mask layer for image and color layers michael@0: SetupMaskLayer(layer, data->mItemClip); michael@0: } michael@0: michael@0: uint32_t flags = 0; michael@0: nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget(); michael@0: // See bug 941095. Not quite ready to disable this. michael@0: bool hidpi = false && widget && widget->GetDefaultScale().scale >= 2; michael@0: if (hidpi) { michael@0: flags |= Layer::CONTENT_DISABLE_SUBPIXEL_AA; michael@0: } michael@0: if (isOpaque && !data->mForceTransparentSurface) { michael@0: flags |= Layer::CONTENT_OPAQUE; michael@0: } else if (data->mNeedComponentAlpha && !hidpi) { michael@0: flags |= Layer::CONTENT_COMPONENT_ALPHA; michael@0: } michael@0: layer->SetContentFlags(flags); michael@0: michael@0: SetFixedPositionLayerData(layer, data->mFixedPosFrameForLayerData); michael@0: michael@0: ThebesLayerData* containingThebesLayerData = michael@0: mLayerBuilder->GetContainingThebesLayerData(); michael@0: if (containingThebesLayerData) { michael@0: gfx3DMatrix matrix = GetTransformToRoot(layer); michael@0: nsIntPoint translatedDest = GetTranslationForThebesLayer(containingThebesLayerData->mLayer); michael@0: matrix.TranslatePost(-gfxPoint3D(translatedDest.x, translatedDest.y, 0)); michael@0: AddTransformedBoundsToRegion(data->mDispatchToContentHitRegion, matrix, michael@0: &containingThebesLayerData->mDispatchToContentHitRegion); michael@0: AddTransformedBoundsToRegion(data->mMaybeHitRegion, matrix, michael@0: &containingThebesLayerData->mMaybeHitRegion); michael@0: // Our definitely-hit region must go to the maybe-hit-region since michael@0: // this function is an approximation. michael@0: gfxMatrix matrix2D; michael@0: bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform(); michael@0: AddTransformedBoundsToRegion(data->mHitRegion, matrix, michael@0: isPrecise ? &containingThebesLayerData->mHitRegion michael@0: : &containingThebesLayerData->mMaybeHitRegion); michael@0: } else { michael@0: EventRegions regions; michael@0: regions.mHitRegion.Swap(&data->mHitRegion); michael@0: // Points whose hit-region status we're not sure about need to be dispatched michael@0: // to the content thread. michael@0: regions.mDispatchToContentHitRegion.Sub(data->mMaybeHitRegion, regions.mHitRegion); michael@0: regions.mDispatchToContentHitRegion.Or(regions.mDispatchToContentHitRegion, michael@0: data->mDispatchToContentHitRegion); michael@0: layer->SetEventRegions(regions); michael@0: } michael@0: michael@0: if (lastIndex > 0) { michael@0: // Since we're going to pop off the last ThebesLayerData, the michael@0: // mVisibleAboveRegion of the second-to-last item will need to include michael@0: // the regions of the last item. michael@0: ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1]; michael@0: nextData->CopyAboveRegion(data); michael@0: } michael@0: michael@0: mThebesLayerDataStack.RemoveElementAt(lastIndex); michael@0: } michael@0: michael@0: static bool michael@0: SuppressComponentAlpha(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem, michael@0: const nsRect& aComponentAlphaBounds) michael@0: { michael@0: const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); michael@0: if (!windowTransparentRegion || windowTransparentRegion->IsEmpty()) michael@0: return false; michael@0: michael@0: // Suppress component alpha for items in the toplevel window that are over michael@0: // the window translucent area michael@0: nsIFrame* f = aItem->Frame(); michael@0: nsIFrame* ref = aBuilder->RootReferenceFrame(); michael@0: if (f->PresContext() != ref->PresContext()) michael@0: return false; michael@0: michael@0: for (nsIFrame* t = f; t; t = t->GetParent()) { michael@0: if (t->IsTransformed()) michael@0: return false; michael@0: } michael@0: michael@0: return windowTransparentRegion->Intersects(aComponentAlphaBounds); michael@0: } michael@0: michael@0: static bool michael@0: WindowHasTransparency(nsDisplayListBuilder* aBuilder) michael@0: { michael@0: const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); michael@0: return windowTransparentRegion && !windowTransparentRegion->IsEmpty(); michael@0: } michael@0: michael@0: void michael@0: ThebesLayerData::Accumulate(ContainerState* aState, michael@0: nsDisplayItem* aItem, michael@0: const nsIntRect& aVisibleRect, michael@0: const nsIntRect& aDrawRect, michael@0: const DisplayItemClip& aClip) michael@0: { michael@0: if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) { michael@0: mForceTransparentSurface = true; michael@0: } michael@0: if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { michael@0: // Disable component alpha. michael@0: // Note that the transform (if any) on the ThebesLayer is always an integer translation so michael@0: // we don't have to factor that in here. michael@0: aItem->DisableComponentAlpha(); michael@0: } michael@0: michael@0: /* Mark as available for conversion to image layer if this is a nsDisplayImage and michael@0: * we are the first visible item in the ThebesLayerData object. michael@0: */ michael@0: if (mVisibleRegion.IsEmpty() && michael@0: aItem->SupportsOptimizingToImage()) { michael@0: mImage = static_cast(aItem); michael@0: } else { michael@0: mImage = nullptr; michael@0: } michael@0: bool clipMatches = mItemClip == aClip; michael@0: mItemClip = aClip; michael@0: michael@0: if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aDrawRect) && michael@0: mVisibleRegion.Contains(aVisibleRect)) { michael@0: // A very common case! Most pages have a ThebesLayer with the page michael@0: // background (opaque) visible and most or all of the page content over the michael@0: // top of that background. michael@0: // The rest of this method won't do anything. mVisibleRegion, mOpaqueRegion michael@0: // and mDrawRegion don't need updating. mVisibleRegion contains aVisibleRect michael@0: // already, mOpaqueRegion contains aDrawRect and therefore whatever michael@0: // the opaque region of the item is. mDrawRegion must contain mOpaqueRegion michael@0: // and therefore aDrawRect. michael@0: NS_ASSERTION(mDrawRegion.Contains(aDrawRect), "Draw region not covered"); michael@0: return; michael@0: } michael@0: michael@0: nscolor uniformColor; michael@0: bool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor); michael@0: michael@0: // Some display items have to exist (so they can set forceTransparentSurface michael@0: // below) but don't draw anything. They'll return true for isUniform but michael@0: // a color with opacity 0. michael@0: if (!isUniform || NS_GET_A(uniformColor) > 0) { michael@0: // Make sure that the visible area is covered by uniform pixels. In michael@0: // particular this excludes cases where the edges of the item are not michael@0: // pixel-aligned (thus the item will not be truly uniform). michael@0: if (isUniform) { michael@0: bool snap; michael@0: nsRect bounds = aItem->GetBounds(aState->mBuilder, &snap); michael@0: if (!aState->ScaleToInsidePixels(bounds, snap).Contains(aVisibleRect)) { michael@0: isUniform = false; michael@0: } michael@0: } michael@0: if (isUniform) { michael@0: if (mVisibleRegion.IsEmpty()) { michael@0: // This color is all we have michael@0: mSolidColor = uniformColor; michael@0: mIsSolidColorInVisibleRegion = true; michael@0: } else if (mIsSolidColorInVisibleRegion && michael@0: mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect)) && michael@0: clipMatches) { michael@0: // we can just blend the colors together michael@0: mSolidColor = NS_ComposeColors(mSolidColor, uniformColor); michael@0: } else { michael@0: mIsSolidColorInVisibleRegion = false; michael@0: } michael@0: } else { michael@0: mIsSolidColorInVisibleRegion = false; michael@0: } michael@0: michael@0: mVisibleRegion.Or(mVisibleRegion, aVisibleRect); michael@0: mVisibleRegion.SimplifyOutward(4); michael@0: mDrawRegion.Or(mDrawRegion, aDrawRect); michael@0: mDrawRegion.SimplifyOutward(4); michael@0: } michael@0: michael@0: bool snap; michael@0: nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &snap); michael@0: if (!opaque.IsEmpty()) { michael@0: nsRegion opaqueClipped; michael@0: nsRegionRectIterator iter(opaque); michael@0: for (const nsRect* r = iter.Next(); r; r = iter.Next()) { michael@0: opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInward(*r)); michael@0: } michael@0: michael@0: nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap); michael@0: michael@0: nsIntRegionRectIterator iter2(opaquePixels); michael@0: for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) { michael@0: // We don't use SimplifyInward here since it's not defined exactly michael@0: // what it will discard. For our purposes the most important case michael@0: // is a large opaque background at the bottom of z-order (e.g., michael@0: // a canvas background), so we need to make sure that the first rect michael@0: // we see doesn't get discarded. michael@0: nsIntRegion tmp; michael@0: tmp.Or(mOpaqueRegion, *r); michael@0: // Opaque display items in chrome documents whose window is partially michael@0: // transparent are always added to the opaque region. This helps ensure michael@0: // that we get as much subpixel-AA as possible in the chrome. michael@0: if (tmp.GetNumRects() <= 4 || michael@0: (WindowHasTransparency(aState->mBuilder) && michael@0: aItem->Frame()->PresContext()->IsChrome())) { michael@0: mOpaqueRegion = tmp; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { michael@0: nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder); michael@0: if (!componentAlpha.IsEmpty()) { michael@0: nsIntRect componentAlphaRect = michael@0: aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect); michael@0: if (!mOpaqueRegion.Contains(componentAlphaRect)) { michael@0: if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) { michael@0: aItem->DisableComponentAlpha(); michael@0: } else { michael@0: mNeedComponentAlpha = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: ThebesLayerData* michael@0: ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, michael@0: const nsIntRect& aVisibleRect, michael@0: const nsIFrame* aAnimatedGeometryRoot, michael@0: const nsPoint& aTopLeft, michael@0: bool aShouldFixToViewport) michael@0: { michael@0: int32_t i; michael@0: int32_t lowestUsableLayerWithScrolledRoot = -1; michael@0: int32_t topmostLayerWithScrolledRoot = -1; michael@0: for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) { michael@0: // Don't let should-fix-to-viewport items share a layer with any other items. michael@0: if (aShouldFixToViewport) { michael@0: ++i; michael@0: break; michael@0: } michael@0: ThebesLayerData* data = mThebesLayerDataStack[i]; michael@0: // Give up if there is content drawn above (in z-order) this layer that michael@0: // intersects aItem's visible region; aItem must be placed in a michael@0: // layer this layer. michael@0: if (data->DrawAboveRegionIntersects(aVisibleRect)) { michael@0: ++i; michael@0: break; michael@0: } michael@0: // If the animated scrolled roots are the same and we can share this layer michael@0: // with the item, note this as a usable layer. michael@0: if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot && michael@0: !data->mSingleItemFixedToViewport) { michael@0: lowestUsableLayerWithScrolledRoot = i; michael@0: if (topmostLayerWithScrolledRoot < 0) { michael@0: topmostLayerWithScrolledRoot = i; michael@0: } michael@0: } michael@0: // If the layer's drawn region intersects the item, stop now since no michael@0: // lower layer will be usable. Do the same if the layer is subject to michael@0: // async transforms, since we don't know where it will really be drawn. michael@0: if (data->DrawRegionIntersects(aVisibleRect)) michael@0: break; michael@0: } michael@0: if (topmostLayerWithScrolledRoot < 0) { michael@0: --i; michael@0: for (; i >= 0; --i) { michael@0: ThebesLayerData* data = mThebesLayerDataStack[i]; michael@0: if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot) { michael@0: topmostLayerWithScrolledRoot = i; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (topmostLayerWithScrolledRoot >= 0) { michael@0: while (uint32_t(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) { michael@0: PopThebesLayerData(); michael@0: } michael@0: } michael@0: michael@0: ThebesLayerData* thebesLayerData = nullptr; michael@0: if (lowestUsableLayerWithScrolledRoot < 0) { michael@0: nsRefPtr layer = michael@0: CreateOrRecycleThebesLayer(aAnimatedGeometryRoot, aItem->ReferenceFrame(), aTopLeft); michael@0: michael@0: thebesLayerData = new ThebesLayerData(); michael@0: mThebesLayerDataStack.AppendElement(thebesLayerData); michael@0: thebesLayerData->mLayer = layer; michael@0: thebesLayerData->mAnimatedGeometryRoot = aAnimatedGeometryRoot; michael@0: thebesLayerData->mFixedPosFrameForLayerData = michael@0: FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport); michael@0: thebesLayerData->mReferenceFrame = aItem->ReferenceFrame(); michael@0: thebesLayerData->mSingleItemFixedToViewport = aShouldFixToViewport; michael@0: michael@0: NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???"); michael@0: *mNewChildLayers.AppendElement() = layer.forget(); michael@0: } else { michael@0: thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot]; michael@0: } michael@0: michael@0: return thebesLayerData; michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: static void michael@0: DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf) michael@0: { michael@0: nsCString string(aItem->Name()); michael@0: string.Append("-"); michael@0: string.AppendInt((uint64_t)aItem); michael@0: fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); michael@0: aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); michael@0: fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); michael@0: } michael@0: #endif michael@0: michael@0: static void michael@0: PaintInactiveLayer(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: nsDisplayItem* aItem, michael@0: gfxContext* aContext, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: // This item has an inactive layer. Render it to a ThebesLayer michael@0: // using a temporary BasicLayerManager. michael@0: BasicLayerManager* basic = static_cast(aManager); michael@0: nsRefPtr context = aContext; michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: int32_t appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem); michael@0: nsIntRect itemVisibleRect = michael@0: aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel); michael@0: michael@0: nsRefPtr surf; michael@0: if (gfxUtils::sDumpPainting) { michael@0: surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size().ToIntSize(), michael@0: gfxContentType::COLOR_ALPHA); michael@0: surf->SetDeviceOffset(-itemVisibleRect.TopLeft()); michael@0: context = new gfxContext(surf); michael@0: } michael@0: #endif michael@0: basic->BeginTransaction(); michael@0: basic->SetTarget(context); michael@0: michael@0: if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) { michael@0: static_cast(aItem)->PaintAsLayer(aBuilder, aCtx, basic); michael@0: if (basic->InTransaction()) { michael@0: basic->AbortTransaction(); michael@0: } michael@0: } else { michael@0: basic->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder); michael@0: } michael@0: FrameLayerBuilder *builder = static_cast(basic->GetUserData(&gLayerManagerLayerBuilder)); michael@0: if (builder) { michael@0: builder->DidEndTransaction(); michael@0: } michael@0: michael@0: basic->SetTarget(nullptr); michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (gfxUtils::sDumpPainting) { michael@0: DumpPaintedImage(aItem, surf); michael@0: michael@0: surf->SetDeviceOffset(gfxPoint(0, 0)); michael@0: aContext->SetSource(surf, itemVisibleRect.TopLeft()); michael@0: aContext->Rectangle(itemVisibleRect); michael@0: aContext->Fill(); michael@0: aItem->SetPainted(); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Chooses a single active scrolled root for the entire display list, used michael@0: * when we are flattening layers. michael@0: */ michael@0: bool michael@0: ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, michael@0: const nsIFrame **aAnimatedGeometryRoot) michael@0: { michael@0: for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { michael@0: LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); michael@0: // Don't use an item that won't be part of any ThebesLayers to pick the michael@0: // active scrolled root. michael@0: if (layerState == LAYER_ACTIVE_FORCE) { michael@0: continue; michael@0: } michael@0: michael@0: // Try using the actual active scrolled root of the backmost item, as that michael@0: // should result in the least invalidation when scrolling. michael@0: *aAnimatedGeometryRoot = michael@0: nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* michael@0: * Iterate through the non-clip items in aList and its descendants. michael@0: * For each item we compute the effective clip rect. Each item is assigned michael@0: * to a layer. We invalidate the areas in ThebesLayers where an item michael@0: * has moved from one ThebesLayer to another. Also, michael@0: * aState->mInvalidThebesContent is invalidated in every ThebesLayer. michael@0: * We set the clip rect for items that generated their own layer, and michael@0: * create a mask layer to do any rounded rect clipping. michael@0: * (ThebesLayers don't need a clip rect on the layer, we clip the items michael@0: * individually when we draw them.) michael@0: * We set the visible rect for all layers, although the actual setting michael@0: * of visible rects for some ThebesLayers is deferred until the calling michael@0: * of ContainerState::Finish. michael@0: */ michael@0: void michael@0: ContainerState::ProcessDisplayItems(const nsDisplayList& aList, michael@0: uint32_t aFlags) michael@0: { michael@0: PROFILER_LABEL("ContainerState", "ProcessDisplayItems"); michael@0: michael@0: const nsIFrame* lastAnimatedGeometryRoot = mContainerReferenceFrame; michael@0: nsPoint topLeft(0,0); michael@0: michael@0: // When NO_COMPONENT_ALPHA is set, items will be flattened into a single michael@0: // layer, so we need to choose which active scrolled root to use for all michael@0: // items. michael@0: if (aFlags & NO_COMPONENT_ALPHA) { michael@0: if (ChooseAnimatedGeometryRoot(aList, &lastAnimatedGeometryRoot)) { michael@0: topLeft = lastAnimatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame); michael@0: } michael@0: } michael@0: michael@0: int32_t maxLayers = nsDisplayItem::MaxActiveLayers(); michael@0: int layerCount = 0; michael@0: michael@0: for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { michael@0: NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item), michael@0: "items in a container layer should all have the same app units per dev pixel"); michael@0: michael@0: nsIntRect itemVisibleRect = michael@0: ScaleToOutsidePixels(item->GetVisibleRect(), false); michael@0: bool snap; michael@0: nsRect itemContent = item->GetBounds(mBuilder, &snap); michael@0: nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap); michael@0: nsDisplayItem::Type itemType = item->GetType(); michael@0: nsIntRect clipRect; michael@0: const DisplayItemClip& itemClip = item->GetClip(); michael@0: if (itemClip.HasClip()) { michael@0: itemContent.IntersectRect(itemContent, itemClip.GetClipRect()); michael@0: clipRect = ScaleToNearestPixels(itemClip.GetClipRect()); michael@0: itemDrawRect.IntersectRect(itemDrawRect, clipRect); michael@0: clipRect.MoveBy(mParameters.mOffset); michael@0: } michael@0: mBounds.UnionRect(mBounds, itemContent); michael@0: itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect); michael@0: michael@0: LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); michael@0: if (layerState == LAYER_INACTIVE && michael@0: nsDisplayItem::ForceActiveLayers()) { michael@0: layerState = LAYER_ACTIVE; michael@0: } michael@0: michael@0: bool forceInactive; michael@0: const nsIFrame* animatedGeometryRoot; michael@0: if (aFlags & NO_COMPONENT_ALPHA) { michael@0: forceInactive = true; michael@0: animatedGeometryRoot = lastAnimatedGeometryRoot; michael@0: } else { michael@0: forceInactive = false; michael@0: if (mManager->IsWidgetLayerManager()) { michael@0: animatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); michael@0: } else { michael@0: // For inactive layer subtrees, splitting content into ThebesLayers michael@0: // based on animated geometry roots is pointless. It's more efficient michael@0: // to build the minimum number of layers. michael@0: animatedGeometryRoot = mContainerAnimatedGeometryRoot; michael@0: } michael@0: if (animatedGeometryRoot != lastAnimatedGeometryRoot) { michael@0: lastAnimatedGeometryRoot = animatedGeometryRoot; michael@0: topLeft = animatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame); michael@0: } michael@0: } michael@0: bool shouldFixToViewport = !animatedGeometryRoot->GetParent() && michael@0: item->ShouldFixToViewport(mBuilder); michael@0: michael@0: if (maxLayers != -1 && layerCount >= maxLayers) { michael@0: forceInactive = true; michael@0: } michael@0: michael@0: // Assign the item to a layer michael@0: if (layerState == LAYER_ACTIVE_FORCE || michael@0: (layerState == LAYER_INACTIVE && !mManager->IsWidgetLayerManager()) || michael@0: (!forceInactive && michael@0: (layerState == LAYER_ACTIVE_EMPTY || michael@0: layerState == LAYER_ACTIVE))) { michael@0: michael@0: layerCount++; michael@0: michael@0: // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata. michael@0: // We should never see an empty layer with any visible content! michael@0: NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY || michael@0: itemVisibleRect.IsEmpty(), michael@0: "State is LAYER_ACTIVE_EMPTY but visible rect is not."); michael@0: michael@0: // As long as the new layer isn't going to be a ThebesLayer, michael@0: // InvalidateForLayerChange doesn't need the new layer pointer. michael@0: // We also need to check the old data now, because BuildLayer michael@0: // can overwrite it. michael@0: InvalidateForLayerChange(item, nullptr, itemClip, topLeft, nullptr); michael@0: michael@0: // If the item would have its own layer but is invisible, just hide it. michael@0: // Note that items without their own layers can't be skipped this michael@0: // way, since their ThebesLayer may decide it wants to draw them michael@0: // into its buffer even if they're currently covered. michael@0: if (itemVisibleRect.IsEmpty() && michael@0: !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) { michael@0: continue; michael@0: } michael@0: michael@0: if (itemType == nsDisplayItem::TYPE_TRANSFORM) { michael@0: mParameters.mAncestorClipRect = itemClip.HasClip() ? &clipRect : nullptr; michael@0: } else { michael@0: mParameters.mAncestorClipRect = nullptr; michael@0: } michael@0: michael@0: // Just use its layer. michael@0: nsRefPtr ownLayer = item->BuildLayer(mBuilder, mManager, mParameters); michael@0: if (!ownLayer) { michael@0: continue; michael@0: } michael@0: michael@0: NS_ASSERTION(!ownLayer->AsThebesLayer(), michael@0: "Should never have created a dedicated Thebes layer!"); michael@0: michael@0: const nsIFrame* fixedPosFrame = michael@0: FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport); michael@0: if (fixedPosFrame) { michael@0: nsIntRegion visibleRegion(itemVisibleRect); michael@0: AdjustLayerDataForFixedPositioning(fixedPosFrame, michael@0: nsIntRegion(itemDrawRect), &visibleRegion); michael@0: itemVisibleRect = visibleRegion.GetBounds(); michael@0: } michael@0: SetFixedPositionLayerData(ownLayer, fixedPosFrame); michael@0: michael@0: nsRect invalid; michael@0: if (item->IsInvalid(invalid)) { michael@0: ownLayer->SetInvalidRectToVisibleRegion(); michael@0: } michael@0: michael@0: // If it's not a ContainerLayer, we need to apply the scale transform michael@0: // ourselves. michael@0: if (!ownLayer->AsContainerLayer()) { michael@0: ownLayer->SetPostScale(mParameters.mXScale, michael@0: mParameters.mYScale); michael@0: } michael@0: michael@0: // Update that layer's clip and visible rects. michael@0: NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager"); michael@0: NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData), michael@0: "We shouldn't have a FrameLayerBuilder-managed layer here!"); michael@0: NS_ASSERTION(itemClip.HasClip() || michael@0: itemClip.GetRoundedRectCount() == 0, michael@0: "If we have rounded rects, we must have a clip rect"); michael@0: // It has its own layer. Update that layer's clip and visible rects. michael@0: if (itemClip.HasClip()) { michael@0: ownLayer->SetClipRect(&clipRect); michael@0: } else { michael@0: ownLayer->SetClipRect(nullptr); michael@0: } michael@0: ThebesLayerData* data = GetTopThebesLayerData(); michael@0: if (data) { michael@0: // Prerendered transform items can be updated without layer building michael@0: // (async animations or an empty transaction), so we treat all other michael@0: // content as being above this so that the transformed layer can correctly michael@0: // move behind other content. michael@0: if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && michael@0: nsDisplayTransform::ShouldPrerenderTransformedContent(mBuilder, michael@0: item->Frame(), michael@0: false)) { michael@0: data->SetAllDrawingAbove(); michael@0: } else { michael@0: data->AddVisibleAboveRegion(itemVisibleRect); michael@0: michael@0: // Add the entire bounds rect to the mDrawAboveRegion. michael@0: // The visible region may be excluding opaque content above the michael@0: // item, and we need to ensure that that content is not placed michael@0: // in a ThebesLayer below the item! michael@0: data->AddDrawAboveRegion(itemDrawRect); michael@0: } michael@0: } michael@0: itemVisibleRect.MoveBy(mParameters.mOffset); michael@0: if (item->SetVisibleRegionOnLayer()) { michael@0: SetVisibleRegionForLayer(ownLayer, ownLayer->GetVisibleRegion(), itemVisibleRect); michael@0: } michael@0: michael@0: // rounded rectangle clipping using mask layers michael@0: // (must be done after visible rect is set on layer) michael@0: if (itemClip.IsRectClippedByRoundedCorner(itemContent)) { michael@0: SetupMaskLayer(ownLayer, itemClip); michael@0: } michael@0: michael@0: ContainerLayer* oldContainer = ownLayer->GetParent(); michael@0: if (oldContainer && oldContainer != mContainerLayer) { michael@0: oldContainer->RemoveChild(ownLayer); michael@0: } michael@0: NS_ASSERTION(!mNewChildLayers.Contains(ownLayer), michael@0: "Layer already in list???"); michael@0: michael@0: mNewChildLayers.AppendElement(ownLayer); michael@0: michael@0: /** michael@0: * No need to allocate geometry for items that aren't michael@0: * part of a ThebesLayer. michael@0: */ michael@0: nsAutoPtr dummy; michael@0: mLayerBuilder->AddLayerDisplayItem(ownLayer, item, michael@0: itemClip, layerState, michael@0: topLeft, nullptr, michael@0: dummy); michael@0: } else { michael@0: ThebesLayerData* data = michael@0: FindThebesLayerFor(item, itemVisibleRect, animatedGeometryRoot, topLeft, michael@0: shouldFixToViewport); michael@0: michael@0: if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) { michael@0: nsDisplayLayerEventRegions* eventRegions = michael@0: static_cast(item); michael@0: data->AccumulateEventRegions(ScaleRegionToOutsidePixels(eventRegions->HitRegion()), michael@0: ScaleRegionToOutsidePixels(eventRegions->MaybeHitRegion()), michael@0: ScaleRegionToOutsidePixels(eventRegions->DispatchToContentHitRegion())); michael@0: } else { michael@0: // check to see if the new item has rounded rect clips in common with michael@0: // other items in the layer michael@0: data->UpdateCommonClipCount(itemClip); michael@0: data->Accumulate(this, item, itemVisibleRect, itemDrawRect, itemClip); michael@0: michael@0: nsAutoPtr geometry(item->AllocateGeometry(mBuilder)); michael@0: InvalidateForLayerChange(item, data->mLayer, itemClip, topLeft, geometry); michael@0: michael@0: mLayerBuilder->AddThebesDisplayItem(data, item, itemClip, michael@0: mContainerFrame, michael@0: layerState, topLeft, michael@0: geometry); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, michael@0: Layer* aNewLayer, michael@0: const DisplayItemClip& aClip, michael@0: const nsPoint& aTopLeft, michael@0: nsDisplayItemGeometry *aGeometry) michael@0: { michael@0: NS_ASSERTION(aItem->GetPerFrameKey(), michael@0: "Display items that render using Thebes must have a key"); michael@0: nsDisplayItemGeometry *oldGeometry = nullptr; michael@0: DisplayItemClip* oldClip = nullptr; michael@0: nsAutoTArray changedFrames; michael@0: bool isInvalid = false; michael@0: Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid); michael@0: if (aNewLayer != oldLayer && oldLayer) { michael@0: // The item has changed layers. michael@0: // Invalidate the old bounds in the old layer and new bounds in the new layer. michael@0: ThebesLayer* t = oldLayer->AsThebesLayer(); michael@0: if (t) { michael@0: // Note that whenever the layer's scale changes, we invalidate the whole thing, michael@0: // so it doesn't matter whether we are using the old scale at last paint michael@0: // or a new scale here michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer); michael@0: } michael@0: #endif michael@0: InvalidatePostTransformRegion(t, michael@0: oldGeometry->ComputeInvalidationRegion(), michael@0: *oldClip, michael@0: mLayerBuilder->GetLastPaintOffset(t)); michael@0: } michael@0: if (aNewLayer) { michael@0: ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer(); michael@0: if (newThebesLayer) { michael@0: InvalidatePostTransformRegion(newThebesLayer, michael@0: aGeometry->ComputeInvalidationRegion(), michael@0: aClip, michael@0: GetTranslationForThebesLayer(newThebesLayer)); michael@0: } michael@0: } michael@0: aItem->NotifyRenderingChanged(); michael@0: return; michael@0: } michael@0: if (!aNewLayer) { michael@0: return; michael@0: } michael@0: michael@0: ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer(); michael@0: if (!newThebesLayer) { michael@0: return; michael@0: } michael@0: michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to michael@0: // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas. michael@0: // If we do get an invalid rect, then we want to add this on top of the change areas. michael@0: nsRect invalid; michael@0: nsRegion combined; michael@0: nsPoint shift = aTopLeft - data->mLastAnimatedGeometryRootOrigin; michael@0: bool notifyRenderingChanged = true; michael@0: if (!oldLayer) { michael@0: // This item is being added for the first time, invalidate its entire area. michael@0: //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this. michael@0: combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion()); michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer); michael@0: } michael@0: #endif michael@0: } else if (isInvalid || (aItem->IsInvalid(invalid) && invalid.IsEmpty())) { michael@0: // Either layout marked item as needing repainting, invalidate the entire old and new areas. michael@0: combined = oldClip->ApplyNonRoundedIntersection(oldGeometry->ComputeInvalidationRegion()); michael@0: combined.MoveBy(shift); michael@0: combined.Or(combined, aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion())); michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer); michael@0: } michael@0: #endif michael@0: } else { michael@0: // Let the display item check for geometry changes and decide what needs to be michael@0: // repainted. michael@0: michael@0: // We have an optimization to cache the drawing background-attachment: fixed canvas michael@0: // background images so we can scroll and just blit them when they are flattened into michael@0: // the same layer as scrolling content. NotifyRenderingChanged is only used to tell michael@0: // the canvas bg image item to purge this cache. We want to be careful not to accidentally michael@0: // purge the cache if we are just invalidating due to scrolling (ie the background image michael@0: // moves on the scrolling layer but it's rendering stays the same) so if michael@0: // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the michael@0: // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls michael@0: // NotifyRenderingChanged if anything changes). michael@0: if (oldGeometry->ComputeInvalidationRegion() == aGeometry->ComputeInvalidationRegion() && michael@0: *oldClip == aClip && invalid.IsEmpty() && changedFrames.Length() == 0) { michael@0: notifyRenderingChanged = false; michael@0: } michael@0: michael@0: oldGeometry->MoveBy(shift); michael@0: aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined); michael@0: oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(), michael@0: aClip, aGeometry->ComputeInvalidationRegion(), michael@0: &combined); michael@0: michael@0: // Add in any rect that the frame specified michael@0: combined.Or(combined, invalid); michael@0: michael@0: for (uint32_t i = 0; i < changedFrames.Length(); i++) { michael@0: combined.Or(combined, changedFrames[i]->GetVisualOverflowRect()); michael@0: } michael@0: michael@0: // Restrict invalidation to the clipped region michael@0: nsRegion clip; michael@0: if (aClip.ComputeRegionInClips(oldClip, shift, &clip)) { michael@0: combined.And(combined, clip); michael@0: } michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: if (!combined.IsEmpty()) { michael@0: printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: if (!combined.IsEmpty()) { michael@0: if (notifyRenderingChanged) { michael@0: aItem->NotifyRenderingChanged(); michael@0: } michael@0: InvalidatePostTransformRegion(newThebesLayer, michael@0: combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel), michael@0: GetTranslationForThebesLayer(newThebesLayer)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData, michael@0: nsDisplayItem* aItem, michael@0: const DisplayItemClip& aClip, michael@0: nsIFrame* aContainerLayerFrame, michael@0: LayerState aLayerState, michael@0: const nsPoint& aTopLeft, michael@0: nsAutoPtr aGeometry) michael@0: { michael@0: ThebesLayer* layer = aLayerData->mLayer; michael@0: ThebesDisplayItemLayerUserData* thebesData = michael@0: static_cast michael@0: (layer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: nsRefPtr tempManager; michael@0: nsIntRect intClip; michael@0: bool hasClip = false; michael@0: if (aLayerState != LAYER_NONE) { michael@0: DisplayItemData *data = GetDisplayItemDataForManager(aItem, layer->Manager()); michael@0: if (data) { michael@0: tempManager = data->mInactiveManager; michael@0: } michael@0: if (!tempManager) { michael@0: tempManager = new BasicLayerManager(); michael@0: } michael@0: michael@0: // We need to grab these before calling AddLayerDisplayItem because it will overwrite them. michael@0: nsRegion clip; michael@0: DisplayItemClip* oldClip = nullptr; michael@0: GetOldLayerFor(aItem, nullptr, &oldClip); michael@0: hasClip = aClip.ComputeRegionInClips(oldClip, michael@0: aTopLeft - thebesData->mLastAnimatedGeometryRootOrigin, michael@0: &clip); michael@0: michael@0: if (hasClip) { michael@0: intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale, michael@0: thebesData->mYScale, michael@0: thebesData->mAppUnitsPerDevPixel); michael@0: } michael@0: } michael@0: michael@0: AddLayerDisplayItem(layer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry); michael@0: michael@0: ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(layer); michael@0: if (entry) { michael@0: entry->mContainerLayerFrame = aContainerLayerFrame; michael@0: if (entry->mContainerLayerGeneration == 0) { michael@0: entry->mContainerLayerGeneration = mContainerLayerGeneration; michael@0: } michael@0: if (tempManager) { michael@0: FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); michael@0: layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData); michael@0: michael@0: tempManager->BeginTransaction(); michael@0: if (mRetainingManager) { michael@0: layerBuilder->DidBeginRetainedLayerTransaction(tempManager); michael@0: } michael@0: michael@0: nsAutoPtr props(LayerProperties::CloneFrom(tempManager->GetRoot())); michael@0: nsRefPtr tmpLayer = michael@0: aItem->BuildLayer(mDisplayListBuilder, tempManager, ContainerLayerParameters()); michael@0: // We have no easy way of detecting if this transaction will ever actually get finished. michael@0: // For now, I've just silenced the warning with nested transactions in BasicLayers.cpp michael@0: if (!tmpLayer) { michael@0: tempManager->EndTransaction(nullptr, nullptr); michael@0: tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr); michael@0: return; michael@0: } michael@0: michael@0: // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been michael@0: // stored in layerBuilder. Manually add it now. michael@0: if (mRetainingManager) { michael@0: #ifdef DEBUG_DISPLAY_ITEM_DATA michael@0: LayerManagerData* parentLmd = static_cast michael@0: (layer->Manager()->GetUserData(&gLayerManagerUserData)); michael@0: LayerManagerData* lmd = static_cast michael@0: (tempManager->GetUserData(&gLayerManagerUserData)); michael@0: lmd->mParent = parentLmd; michael@0: #endif michael@0: layerBuilder->StoreDataForFrame(aItem, tmpLayer, LAYER_ACTIVE); michael@0: } michael@0: michael@0: tempManager->SetRoot(tmpLayer); michael@0: layerBuilder->WillEndTransaction(); michael@0: tempManager->AbortTransaction(); michael@0: michael@0: nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForThebesLayer(layer); michael@0: props->MoveBy(-offset); michael@0: nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr); michael@0: if (aLayerState == LAYER_SVG_EFFECTS) { michael@0: invalid = nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(aItem->Frame(), michael@0: aItem->ToReferenceFrame(), michael@0: invalid); michael@0: } michael@0: if (!invalid.IsEmpty()) { michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { michael@0: printf_stderr("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), layer); michael@0: } michael@0: #endif michael@0: if (hasClip) { michael@0: invalid.And(invalid, intClip); michael@0: } michael@0: michael@0: invalid.ScaleRoundOut(thebesData->mXScale, thebesData->mYScale); michael@0: InvalidatePostTransformRegion(layer, invalid, michael@0: GetTranslationForThebesLayer(layer)); michael@0: } michael@0: } michael@0: ClippedDisplayItem* cdi = michael@0: entry->mItems.AppendElement(ClippedDisplayItem(aItem, michael@0: mContainerLayerGeneration)); michael@0: cdi->mInactiveLayerManager = tempManager; michael@0: } michael@0: } michael@0: michael@0: FrameLayerBuilder::DisplayItemData* michael@0: FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState) michael@0: { michael@0: DisplayItemData* oldData = GetDisplayItemDataForManager(aItem, mRetainingManager); michael@0: if (oldData) { michael@0: if (!oldData->mUsed) { michael@0: oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration, aItem); michael@0: } michael@0: return oldData; michael@0: } michael@0: michael@0: LayerManagerData* lmd = static_cast michael@0: (mRetainingManager->GetUserData(&gLayerManagerUserData)); michael@0: michael@0: nsRefPtr data = michael@0: new DisplayItemData(lmd, aItem->GetPerFrameKey(), michael@0: aLayer, aState, mContainerLayerGeneration); michael@0: michael@0: data->AddFrame(aItem->Frame()); michael@0: michael@0: nsAutoTArray mergedFrames; michael@0: aItem->GetMergedFrames(&mergedFrames); michael@0: michael@0: for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { michael@0: data->AddFrame(mergedFrames[i]); michael@0: } michael@0: michael@0: lmd->mDisplayItems.PutEntry(data); michael@0: return data; michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame, michael@0: uint32_t aDisplayItemKey, michael@0: Layer* aLayer, michael@0: LayerState aState) michael@0: { michael@0: DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey); michael@0: if (oldData && oldData->mFrameList.Length() == 1) { michael@0: oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration); michael@0: return; michael@0: } michael@0: michael@0: LayerManagerData* lmd = static_cast michael@0: (mRetainingManager->GetUserData(&gLayerManagerUserData)); michael@0: michael@0: nsRefPtr data = michael@0: new DisplayItemData(lmd, aDisplayItemKey, aLayer, michael@0: aState, mContainerLayerGeneration); michael@0: michael@0: data->AddFrame(aFrame); michael@0: michael@0: lmd->mDisplayItems.PutEntry(data); michael@0: } michael@0: michael@0: FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem() michael@0: { michael@0: if (mInactiveLayerManager) { michael@0: BasicLayerManager* basic = static_cast(mInactiveLayerManager.get()); michael@0: basic->SetUserData(&gLayerManagerLayerBuilder, nullptr); michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer, michael@0: nsDisplayItem* aItem, michael@0: const DisplayItemClip& aClip, michael@0: LayerState aLayerState, michael@0: const nsPoint& aTopLeft, michael@0: BasicLayerManager* aManager, michael@0: nsAutoPtr aGeometry) michael@0: { michael@0: if (aLayer->Manager() != mRetainingManager) michael@0: return; michael@0: michael@0: DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState); michael@0: ThebesLayer *t = aLayer->AsThebesLayer(); michael@0: if (t) { michael@0: data->mGeometry = aGeometry; michael@0: data->mClip = aClip; michael@0: } michael@0: data->mInactiveManager = aManager; michael@0: } michael@0: michael@0: nsIntPoint michael@0: FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer) michael@0: { michael@0: ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); michael@0: if (entry) { michael@0: if (entry->mContainerLayerGeneration == 0) { michael@0: entry->mContainerLayerGeneration = mContainerLayerGeneration; michael@0: } michael@0: if (entry->mHasExplicitLastPaintOffset) michael@0: return entry->mLastPaintOffset; michael@0: } michael@0: return GetTranslationForThebesLayer(aLayer); michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer) michael@0: { michael@0: ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); michael@0: if (entry) { michael@0: if (entry->mContainerLayerGeneration == 0) { michael@0: entry->mContainerLayerGeneration = mContainerLayerGeneration; michael@0: } michael@0: entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer); michael@0: entry->mHasExplicitLastPaintOffset = true; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: FrameLayerBuilder::CheckInLayerTreeCompressionMode() michael@0: { michael@0: if (mInLayerTreeCompressionMode) { michael@0: return true; michael@0: } michael@0: michael@0: // If we wanted to be in layer tree compression mode, but weren't, then scheduled michael@0: // a delayed repaint where we will be. michael@0: mRootPresContext->PresShell()->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DELAYED_COMPRESS); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: ContainerState::CollectOldLayers() michael@0: { michael@0: for (Layer* layer = mContainerLayer->GetFirstChild(); layer; michael@0: layer = layer->GetNextSibling()) { michael@0: NS_ASSERTION(!layer->HasUserData(&gMaskLayerUserData), michael@0: "Mask layer in layer tree; could not be recycled."); michael@0: if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) { michael@0: NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type"); michael@0: mRecycledThebesLayers.AppendElement(static_cast(layer)); michael@0: } michael@0: michael@0: if (Layer* maskLayer = layer->GetMaskLayer()) { michael@0: NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE, michael@0: "Could not recycle mask layer, unsupported layer type."); michael@0: mRecycledMaskImageLayers.Put(layer, static_cast(maskLayer)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData) michael@0: { michael@0: while (!mThebesLayerDataStack.IsEmpty()) { michael@0: PopThebesLayerData(); michael@0: } michael@0: michael@0: uint32_t textContentFlags = 0; michael@0: michael@0: // Make sure that current/existing layers are added to the parent and are michael@0: // in the correct order. michael@0: Layer* layer = nullptr; michael@0: for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) { michael@0: Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get(); michael@0: layer = mNewChildLayers[i]; michael@0: michael@0: if (!layer->GetVisibleRegion().IsEmpty()) { michael@0: textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA; michael@0: } michael@0: michael@0: if (!layer->GetParent()) { michael@0: // This is not currently a child of the container, so just add it michael@0: // now. michael@0: mContainerLayer->InsertAfter(layer, prevChild); michael@0: continue; michael@0: } michael@0: michael@0: NS_ASSERTION(layer->GetParent() == mContainerLayer, michael@0: "Layer shouldn't be the child of some other container"); michael@0: if (layer->GetPrevSibling() != prevChild) { michael@0: mContainerLayer->RepositionChild(layer, prevChild); michael@0: } michael@0: } michael@0: michael@0: // Remove old layers that have become unused. michael@0: if (!layer) { michael@0: layer = mContainerLayer->GetFirstChild(); michael@0: } else { michael@0: layer = layer->GetNextSibling(); michael@0: } michael@0: while (layer) { michael@0: Layer *layerToRemove = layer; michael@0: layer = layer->GetNextSibling(); michael@0: mContainerLayer->RemoveChild(layerToRemove); michael@0: } michael@0: michael@0: *aTextContentFlags = textContentFlags; michael@0: } michael@0: michael@0: static inline gfxSize RoundToFloatPrecision(const gfxSize& aSize) michael@0: { michael@0: return gfxSize(float(aSize.width), float(aSize.height)); michael@0: } michael@0: michael@0: static bool michael@0: ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, michael@0: nsDisplayListBuilder* aDisplayListBuilder, michael@0: nsIFrame* aContainerFrame, michael@0: const gfx3DMatrix* aTransform, michael@0: const ContainerLayerParameters& aIncomingScale, michael@0: ContainerLayer* aLayer, michael@0: LayerState aState, michael@0: ContainerLayerParameters& aOutgoingScale) michael@0: { michael@0: nsIntPoint offset; michael@0: michael@0: gfx3DMatrix transform = michael@0: gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0); michael@0: if (aTransform) { michael@0: // aTransform is applied first, then the scale is applied to the result michael@0: transform = (*aTransform)*transform; michael@0: // Set any matrix entries close to integers to be those exact integers. michael@0: // This protects against floating-point inaccuracies causing problems michael@0: // in the checks below. michael@0: transform.NudgeToIntegers(); michael@0: } michael@0: gfxMatrix transform2d; michael@0: if (aContainerFrame && michael@0: (aState == LAYER_INACTIVE || aState == LAYER_SVG_EFFECTS) && michael@0: (!aTransform || (aTransform->Is2D(&transform2d) && michael@0: !transform2d.HasNonTranslation()))) { michael@0: // When we have an inactive ContainerLayer, translate the container by the offset to the michael@0: // reference frame (and offset all child layers by the reverse) so that the coordinate michael@0: // space of the child layers isn't affected by scrolling. michael@0: // This gets confusing for complicated transform (since we'd have to compute the scale michael@0: // factors for the matrix), so we don't bother. Any frames that are building an nsDisplayTransform michael@0: // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't michael@0: // matter. michael@0: nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame); michael@0: nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: offset = nsIntPoint( michael@0: NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale), michael@0: NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale)); michael@0: } michael@0: transform = transform * gfx3DMatrix::Translation(offset.x + aIncomingScale.mOffset.x, offset.y + aIncomingScale.mOffset.y, 0); michael@0: michael@0: if (transform.IsSingular()) { michael@0: return false; michael@0: } michael@0: michael@0: bool canDraw2D = transform.CanDraw2D(&transform2d); michael@0: gfxSize scale; michael@0: // XXX Should we do something for 3D transforms? michael@0: if (canDraw2D) { michael@0: // If the container's transform is animated off main thread, fix a suitable scale size michael@0: // for animation michael@0: if (aContainerFrame->GetContent() && michael@0: nsLayoutUtils::HasAnimationsForCompositor( michael@0: aContainerFrame->GetContent(), eCSSProperty_transform)) { michael@0: scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(aContainerFrame->GetContent()); michael@0: } else { michael@0: // Scale factors are normalized to a power of 2 to reduce the number of resolution changes michael@0: scale = RoundToFloatPrecision(transform2d.ScaleFactors(true)); michael@0: // For frames with a changing transform that's not just a translation, michael@0: // round scale factors up to nearest power-of-2 boundary so that we don't michael@0: // keep having to redraw the content as it scales up and down. Rounding up to nearest michael@0: // power-of-2 boundary ensures we never scale up, only down --- avoiding michael@0: // jaggies. It also ensures we never scale down by more than a factor of 2, michael@0: // avoiding bad downscaling quality. michael@0: gfxMatrix frameTransform; michael@0: if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform) && michael@0: aTransform && michael@0: (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) { michael@0: // Don't clamp the scale factor when the new desired scale factor matches the old one michael@0: // or it was previously unscaled. michael@0: bool clamp = true; michael@0: Matrix oldFrameTransform2d; michael@0: if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) { michael@0: gfxSize oldScale = RoundToFloatPrecision(ThebesMatrix(oldFrameTransform2d).ScaleFactors(true)); michael@0: if (oldScale == scale || oldScale == gfxSize(1.0, 1.0)) { michael@0: clamp = false; michael@0: } michael@0: } michael@0: if (clamp) { michael@0: scale.width = gfxUtils::ClampToScaleFactor(scale.width); michael@0: scale.height = gfxUtils::ClampToScaleFactor(scale.height); michael@0: } michael@0: } else { michael@0: // XXX Do we need to move nearly-integer values to integers here? michael@0: } michael@0: } michael@0: // If the scale factors are too small, just use 1.0. The content is being michael@0: // scaled out of sight anyway. michael@0: if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) { michael@0: scale = gfxSize(1.0, 1.0); michael@0: } michael@0: } else { michael@0: scale = gfxSize(1.0, 1.0); michael@0: } michael@0: michael@0: // Store the inverse of our resolution-scale on the layer michael@0: Matrix4x4 baseTransform; michael@0: ToMatrix4x4(transform, baseTransform); michael@0: aLayer->SetBaseTransform(baseTransform); michael@0: aLayer->SetPreScale(1.0f/float(scale.width), michael@0: 1.0f/float(scale.height)); michael@0: aLayer->SetInheritedScale(aIncomingScale.mXScale, michael@0: aIncomingScale.mYScale); michael@0: michael@0: aOutgoingScale = michael@0: ContainerLayerParameters(scale.width, scale.height, -offset, aIncomingScale); michael@0: if (aTransform) { michael@0: aOutgoingScale.mInTransformedSubtree = true; michael@0: if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform)) { michael@0: aOutgoingScale.mInActiveTransformedSubtree = true; michael@0: } michael@0: } michael@0: bool isRetained = aLayer->Manager()->IsWidgetLayerManager(); michael@0: if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) { michael@0: aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: FrameLayerBuilder::RestoreDisplayItemData(nsRefPtrHashKey* aEntry, void* aUserArg) michael@0: { michael@0: DisplayItemData* data = aEntry->GetKey(); michael@0: uint32_t *generation = static_cast(aUserArg); michael@0: michael@0: if (data->mUsed && data->mContainerLayerGeneration >= *generation) { michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: /* static */ PLDHashOperator michael@0: FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg) michael@0: { michael@0: uint32_t *generation = static_cast(aUserArg); michael@0: michael@0: if (aEntry->mContainerLayerGeneration >= *generation) { michael@0: // We can just remove these items rather than attempting to revert them michael@0: // because we're going to want to invalidate everything when transitioning michael@0: // to component alpha flattening. michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < aEntry->mItems.Length(); i++) { michael@0: if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) { michael@0: aEntry->mItems.TruncateLength(i); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: } michael@0: michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: already_AddRefed michael@0: FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, michael@0: LayerManager* aManager, michael@0: nsIFrame* aContainerFrame, michael@0: nsDisplayItem* aContainerItem, michael@0: const nsDisplayList& aChildren, michael@0: const ContainerLayerParameters& aParameters, michael@0: const gfx3DMatrix* aTransform, michael@0: uint32_t aFlags) michael@0: { michael@0: uint32_t containerDisplayItemKey = michael@0: aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO; michael@0: NS_ASSERTION(aContainerFrame, "Container display items here should have a frame"); michael@0: NS_ASSERTION(!aContainerItem || michael@0: aContainerItem->Frame() == aContainerFrame, michael@0: "Container display item must match given frame"); michael@0: michael@0: if (!aParameters.mXScale || !aParameters.mYScale) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr containerLayer; michael@0: if (aManager == mRetainingManager) { michael@0: // Using GetOldLayerFor will search merged frames, as well as the underlying michael@0: // frame. The underlying frame can change when a page scrolls, so this michael@0: // avoids layer recreation in the situation that a new underlying frame is michael@0: // picked for a layer. michael@0: Layer* oldLayer = nullptr; michael@0: if (aContainerItem) { michael@0: oldLayer = GetOldLayerFor(aContainerItem); michael@0: } else { michael@0: DisplayItemData *data = GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey); michael@0: if (data) { michael@0: oldLayer = data->mLayer; michael@0: } michael@0: } michael@0: michael@0: if (oldLayer) { michael@0: NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager"); michael@0: if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) { michael@0: // The old layer for this item is actually our ThebesLayer michael@0: // because we rendered its layer into that ThebesLayer. So we michael@0: // don't actually have a retained container layer. michael@0: } else { michael@0: NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER, michael@0: "Wrong layer type"); michael@0: containerLayer = static_cast(oldLayer); michael@0: containerLayer->SetMaskLayer(nullptr); michael@0: } michael@0: } michael@0: } michael@0: if (!containerLayer) { michael@0: // No suitable existing layer was found. michael@0: containerLayer = aManager->CreateContainerLayer(); michael@0: if (!containerLayer) michael@0: return nullptr; michael@0: } michael@0: michael@0: LayerState state = aContainerItem ? aContainerItem->GetLayerState(aBuilder, aManager, aParameters) : LAYER_ACTIVE; michael@0: if (state == LAYER_INACTIVE && michael@0: nsDisplayItem::ForceActiveLayers()) { michael@0: state = LAYER_ACTIVE; michael@0: } michael@0: michael@0: if (aContainerItem && state == LAYER_ACTIVE_EMPTY) { michael@0: // Empty layers only have metadata and should never have display items. We michael@0: // early exit because later, invalidation will walk up the frame tree to michael@0: // determine which thebes layer gets invalidated. Since an empty layer michael@0: // should never have anything to paint, it should never be invalidated. michael@0: NS_ASSERTION(aChildren.IsEmpty(), "Should have no children"); michael@0: return containerLayer.forget(); michael@0: } michael@0: michael@0: ContainerLayerParameters scaleParameters; michael@0: if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame, aTransform, aParameters, michael@0: containerLayer, state, scaleParameters)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t oldGeneration = mContainerLayerGeneration; michael@0: mContainerLayerGeneration = ++mMaxContainerLayerGeneration; michael@0: michael@0: nsRefPtr thebesLayerInvalidRegion = nullptr; michael@0: if (mRetainingManager) { michael@0: if (aContainerItem) { michael@0: StoreDataForFrame(aContainerItem, containerLayer, LAYER_ACTIVE); michael@0: } else { michael@0: StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE); michael@0: } michael@0: } michael@0: michael@0: LayerManagerData* data = static_cast michael@0: (aManager->GetUserData(&gLayerManagerUserData)); michael@0: michael@0: nsRect bounds; michael@0: nsIntRect pixBounds; michael@0: int32_t appUnitsPerDevPixel; michael@0: uint32_t stateFlags = 0; michael@0: if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) && michael@0: mRetainingManager && !mRetainingManager->AreComponentAlphaLayersEnabled()) { michael@0: stateFlags = ContainerState::NO_COMPONENT_ALPHA; michael@0: } michael@0: uint32_t flags; michael@0: while (true) { michael@0: ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(), michael@0: aContainerFrame, aContainerItem, michael@0: containerLayer, scaleParameters); michael@0: michael@0: state.ProcessDisplayItems(aChildren, stateFlags); michael@0: michael@0: // Set CONTENT_COMPONENT_ALPHA if any of our children have it. michael@0: // This is suboptimal ... a child could have text that's over transparent michael@0: // pixels in its own layer, but over opaque parts of previous siblings. michael@0: state.Finish(&flags, data); michael@0: bounds = state.GetChildrenBounds(); michael@0: pixBounds = state.ScaleToOutsidePixels(bounds, false); michael@0: appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel(); michael@0: michael@0: if ((flags & Layer::CONTENT_COMPONENT_ALPHA) && michael@0: mRetainingManager && michael@0: !mRetainingManager->AreComponentAlphaLayersEnabled() && michael@0: !stateFlags) { michael@0: // Since we don't want any component alpha layers on BasicLayers, we repeat michael@0: // the layer building process with this explicitely forced off. michael@0: // We restore the previous FrameLayerBuilder state since the first set michael@0: // of layer building will have changed it. michael@0: stateFlags = ContainerState::NO_COMPONENT_ALPHA; michael@0: data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData, michael@0: &mContainerLayerGeneration); michael@0: mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries, michael@0: &mContainerLayerGeneration); michael@0: aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA); michael@0: continue; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds"); michael@0: pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y)); michael@0: if (aParameters.mAncestorClipRect && !(aFlags & CONTAINER_NOT_CLIPPED_BY_ANCESTORS)) { michael@0: SetVisibleRegionForLayer(containerLayer, nsIntRegion(pixBounds), michael@0: *aParameters.mAncestorClipRect); michael@0: } else { michael@0: containerLayer->SetVisibleRegion(pixBounds); michael@0: } michael@0: // Make sure that rounding the visible region out didn't add any area michael@0: // we won't paint michael@0: if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) { michael@0: bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale); michael@0: if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) { michael@0: // Clear CONTENT_COMPONENT_ALPHA michael@0: flags = Layer::CONTENT_OPAQUE; michael@0: } michael@0: } michael@0: containerLayer->SetContentFlags(flags); michael@0: michael@0: mContainerLayerGeneration = oldGeneration; michael@0: nsPresContext::ClearNotifySubDocInvalidationData(containerLayer); michael@0: michael@0: return containerLayer.forget(); michael@0: } michael@0: michael@0: Layer* michael@0: FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem) michael@0: { michael@0: Layer* layer = GetOldLayerFor(aItem); michael@0: if (!layer) michael@0: return nullptr; michael@0: if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) { michael@0: // This layer was created to render Thebes-rendered content for this michael@0: // display item. The display item should not use it for its own michael@0: // layer rendering. michael@0: return nullptr; michael@0: } michael@0: layer->SetMaskLayer(nullptr); michael@0: return layer; michael@0: } michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager) michael@0: { michael@0: LayerManagerData* data = static_cast michael@0: (aManager->GetUserData(&gLayerManagerUserData)); michael@0: if (data) { michael@0: data->mInvalidateAllLayers = true; michael@0: } michael@0: } michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame) michael@0: { michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (array) { michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: array->ElementAt(i)->mParent->mInvalidateAllLayers = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: Layer* michael@0: FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey) michael@0: { michael@0: //TODO: This isn't completely correct, since a frame could exist as a layer michael@0: // in the normal widget manager, and as a different layer (or no layer) michael@0: // in the secondary manager michael@0: michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (array) { michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: DisplayItemData *element = array->ElementAt(i); michael@0: if (!element->mParent->mLayerManager->IsWidgetLayerManager()) { michael@0: continue; michael@0: } michael@0: if (element->mDisplayItemKey == aDisplayItemKey) { michael@0: if (element->mOptLayer) { michael@0: return element->mOptLayer; michael@0: } michael@0: michael@0: Layer* layer = element->mLayer; michael@0: if (!layer->HasUserData(&gColorLayerUserData) && michael@0: !layer->HasUserData(&gImageLayerUserData) && michael@0: !layer->HasUserData(&gThebesDisplayItemLayerUserData)) { michael@0: return layer; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: static gfxSize michael@0: PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale, michael@0: const gfxSize& aScale) michael@0: { michael@0: gfx3DMatrix transform = michael@0: gfx3DMatrix::ScalingMatrix(aScale.width, aScale.height, 1.0); michael@0: if (aFrame != aAncestorWithScale) { michael@0: // aTransform is applied first, then the scale is applied to the result michael@0: transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform; michael@0: } michael@0: gfxMatrix transform2d; michael@0: if (transform.CanDraw2D(&transform2d)) { michael@0: return transform2d.ScaleFactors(true); michael@0: } michael@0: return gfxSize(1.0, 1.0); michael@0: } michael@0: michael@0: gfxSize michael@0: FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame* aFrame) michael@0: { michael@0: nsIFrame* last; michael@0: for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { michael@0: last = f; michael@0: michael@0: if (nsLayoutUtils::IsPopup(f)) { michael@0: // Don't examine ancestors of a popup. It won't make sense to check michael@0: // the transform from some content inside the popup to some content michael@0: // which is an ancestor of the popup. michael@0: break; michael@0: } michael@0: michael@0: nsTArray *array = michael@0: reinterpret_cast*>(aFrame->Properties().Get(LayerManagerDataProperty())); michael@0: if (!array) { michael@0: continue; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < array->Length(); i++) { michael@0: Layer* layer = array->ElementAt(i)->mLayer; michael@0: ContainerLayer* container = layer->AsContainerLayer(); michael@0: if (!container || michael@0: !layer->Manager()->IsWidgetLayerManager()) { michael@0: continue; michael@0: } michael@0: for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) { michael@0: ThebesDisplayItemLayerUserData* data = michael@0: static_cast michael@0: (l->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: if (data) { michael@0: return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale)); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return PredictScaleForContent(aFrame, last, michael@0: last->PresContext()->PresShell()->GetResolution()); michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder) michael@0: { michael@0: bool snap; michael@0: nsRect appUnitBounds = aItem->GetBounds(aBuilder, &snap); michael@0: gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height); michael@0: bounds.ScaleInverse(aDest->AppUnitsPerDevPixel()); michael@0: michael@0: nsRefPtr surf = michael@0: gfxPlatform::GetPlatform()->CreateOffscreenSurface(IntSize(bounds.width, bounds.height), michael@0: gfxContentType::COLOR_ALPHA); michael@0: surf->SetDeviceOffset(-bounds.TopLeft()); michael@0: nsRefPtr context = new gfxContext(surf); michael@0: nsRefPtr ctx = new nsRenderingContext(); michael@0: ctx->Init(aDest->DeviceContext(), context); michael@0: michael@0: aItem->Paint(aBuilder, ctx); michael@0: DumpPaintedImage(aItem, surf); michael@0: aItem->SetPainted(); michael@0: michael@0: surf->SetDeviceOffset(gfxPoint(0, 0)); michael@0: aDest->ThebesContext()->SetSource(surf, bounds.TopLeft()); michael@0: aDest->ThebesContext()->Rectangle(bounds); michael@0: aDest->ThebesContext()->Fill(); michael@0: } michael@0: #endif michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray& aItems, michael@0: nsDisplayListBuilder *aBuilder, michael@0: const nsIntRegion& aRegionToDraw, michael@0: const nsIntPoint& aOffset, michael@0: int32_t aAppUnitsPerDevPixel, michael@0: float aXScale, michael@0: float aYScale) michael@0: { michael@0: uint32_t i; michael@0: // Update visible regions. We need perform visibility analysis again michael@0: // because we may be asked to draw into part of a ThebesLayer that michael@0: // isn't actually visible in the window (e.g., because a ThebesLayer michael@0: // expanded its visible region to a rectangle internally), in which michael@0: // case the mVisibleRect stored in the display item may be wrong. michael@0: nsRegion visible = aRegionToDraw.ToAppUnits(aAppUnitsPerDevPixel); michael@0: visible.MoveBy(NSIntPixelsToAppUnits(aOffset.x, aAppUnitsPerDevPixel), michael@0: NSIntPixelsToAppUnits(aOffset.y, aAppUnitsPerDevPixel)); michael@0: visible.ScaleInverseRoundOut(aXScale, aYScale); michael@0: michael@0: for (i = aItems.Length(); i > 0; --i) { michael@0: ClippedDisplayItem* cdi = &aItems[i - 1]; michael@0: const DisplayItemClip& clip = cdi->mItem->GetClip(); michael@0: michael@0: NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == aAppUnitsPerDevPixel, michael@0: "a thebes layer should contain items only at the same zoom"); michael@0: michael@0: NS_ABORT_IF_FALSE(clip.HasClip() || michael@0: clip.GetRoundedRectCount() == 0, michael@0: "If we have rounded rects, we must have a clip rect"); michael@0: michael@0: if (!clip.IsRectAffectedByClip(visible.GetBounds())) { michael@0: cdi->mItem->RecomputeVisibility(aBuilder, &visible); michael@0: continue; michael@0: } michael@0: michael@0: // Do a little dance to account for the fact that we're clipping michael@0: // to cdi->mClipRect michael@0: nsRegion clipped; michael@0: clipped.And(visible, clip.NonRoundedIntersection()); michael@0: nsRegion finalClipped = clipped; michael@0: cdi->mItem->RecomputeVisibility(aBuilder, &finalClipped); michael@0: // If we have rounded clip rects, don't subtract from the visible michael@0: // region since we aren't displaying everything inside the rect. michael@0: if (clip.GetRoundedRectCount() == 0) { michael@0: nsRegion removed; michael@0: removed.Sub(clipped, finalClipped); michael@0: nsRegion newVisible; michael@0: newVisible.Sub(visible, removed); michael@0: // Don't let the visible region get too complex. michael@0: if (newVisible.GetNumRects() <= 15) { michael@0: visible = newVisible; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: FrameLayerBuilder::PaintItems(nsTArray& aItems, michael@0: const nsIntRect& aRect, michael@0: gfxContext *aContext, michael@0: nsRenderingContext *aRC, michael@0: nsDisplayListBuilder* aBuilder, michael@0: nsPresContext* aPresContext, michael@0: const nsIntPoint& aOffset, michael@0: float aXScale, float aYScale, michael@0: int32_t aCommonClipCount) michael@0: { michael@0: int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel(); michael@0: nsRect boundRect = aRect.ToAppUnits(appUnitsPerDevPixel); michael@0: boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel), michael@0: NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel)); michael@0: boundRect.ScaleInverseRoundOut(aXScale, aYScale); michael@0: michael@0: DisplayItemClip currentClip; michael@0: bool currentClipIsSetInContext = false; michael@0: DisplayItemClip tmpClip; michael@0: michael@0: for (uint32_t i = 0; i < aItems.Length(); ++i) { michael@0: ClippedDisplayItem* cdi = &aItems[i]; michael@0: michael@0: nsRect paintRect = cdi->mItem->GetVisibleRect().Intersect(boundRect); michael@0: if (paintRect.IsEmpty()) michael@0: continue; michael@0: michael@0: // If the new desired clip state is different from the current state, michael@0: // update the clip. michael@0: const DisplayItemClip* clip = &cdi->mItem->GetClip(); michael@0: if (clip->GetRoundedRectCount() > 0 && michael@0: !clip->IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) { michael@0: tmpClip = *clip; michael@0: tmpClip.RemoveRoundedCorners(); michael@0: clip = &tmpClip; michael@0: } michael@0: if (currentClipIsSetInContext != clip->HasClip() || michael@0: (clip->HasClip() && *clip != currentClip)) { michael@0: if (currentClipIsSetInContext) { michael@0: aContext->Restore(); michael@0: } michael@0: currentClipIsSetInContext = clip->HasClip(); michael@0: if (currentClipIsSetInContext) { michael@0: currentClip = *clip; michael@0: aContext->Save(); michael@0: NS_ASSERTION(aCommonClipCount < 100, michael@0: "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong."); michael@0: currentClip.ApplyTo(aContext, aPresContext, aCommonClipCount); michael@0: aContext->NewPath(); michael@0: } michael@0: } michael@0: michael@0: if (cdi->mInactiveLayerManager) { michael@0: PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aRC); michael@0: } else { michael@0: nsIFrame* frame = cdi->mItem->Frame(); michael@0: frame->AddStateBits(NS_FRAME_PAINTED_THEBES); michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: michael@0: if (gfxUtils::sDumpPainting) { michael@0: DebugPaintItem(aRC, cdi->mItem, aBuilder); michael@0: } else { michael@0: #else michael@0: { michael@0: #endif michael@0: cdi->mItem->Paint(aBuilder, aRC); michael@0: } michael@0: } michael@0: michael@0: if (CheckDOMModified()) michael@0: break; michael@0: } michael@0: michael@0: if (currentClipIsSetInContext) { michael@0: aContext->Restore(); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Returns true if it is preferred to draw the list of display michael@0: * items separately for each rect in the visible region rather michael@0: * than clipping to a complex region. michael@0: */ michael@0: static bool ShouldDrawRectsSeparately(gfxContext* aContext, DrawRegionClip aClip) michael@0: { michael@0: if (!gfxPrefs::LayoutPaintRectsSeparately() || michael@0: aContext->IsCairo() || michael@0: aClip == DrawRegionClip::CLIP_NONE) { michael@0: return false; michael@0: } michael@0: michael@0: DrawTarget *dt = aContext->GetDrawTarget(); michael@0: return dt->GetType() == BackendType::DIRECT2D; michael@0: } michael@0: michael@0: static void DrawForcedBackgroundColor(gfxContext* aContext, Layer* aLayer, nscolor aBackgroundColor) michael@0: { michael@0: if (NS_GET_A(aBackgroundColor) > 0) { michael@0: nsIntRect r = aLayer->GetVisibleRegion().GetBounds(); michael@0: aContext->NewPath(); michael@0: aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); michael@0: aContext->SetColor(gfxRGBA(aBackgroundColor)); michael@0: aContext->Fill(); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * A note on residual transforms: michael@0: * michael@0: * In a transformed subtree we sometimes apply the ThebesLayer's michael@0: * "residual transform" when drawing content into the ThebesLayer. michael@0: * This is a translation by components in the range [-0.5,0.5) provided michael@0: * by the layer system; applying the residual transform followed by the michael@0: * transforms used by layer compositing ensures that the subpixel alignment michael@0: * of the content of the ThebesLayer exactly matches what it would be if michael@0: * we used cairo/Thebes to draw directly to the screen without going through michael@0: * retained layer buffers. michael@0: * michael@0: * The visible and valid regions of the ThebesLayer are computed without michael@0: * knowing the residual transform (because we don't know what the residual michael@0: * transform is going to be until we've built the layer tree!). So we have to michael@0: * consider whether content painted in the range [x, xmost) might be painted michael@0: * outside the visible region we computed for that content. The visible region michael@0: * would be [floor(x), ceil(xmost)). The content would be rendered at michael@0: * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could michael@0: * indeed fall outside the computed visible region, which is not a big deal; michael@0: * similar issues already arise when we snap cliprects to nearest pixels. michael@0: * Note that if the rendering of the content is snapped to nearest pixels --- michael@0: * which it often is --- then the content is actually rendered at michael@0: * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r) michael@0: * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content michael@0: * always falls within the visible region we computed. michael@0: */ michael@0: michael@0: /* static */ void michael@0: FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer, michael@0: gfxContext* aContext, michael@0: const nsIntRegion& aRegionToDraw, michael@0: DrawRegionClip aClip, michael@0: const nsIntRegion& aRegionToInvalidate, michael@0: void* aCallbackData) michael@0: { michael@0: PROFILER_LABEL("gfx", "DrawThebesLayer"); michael@0: michael@0: nsDisplayListBuilder* builder = static_cast michael@0: (aCallbackData); michael@0: michael@0: FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder(); michael@0: NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!"); michael@0: michael@0: if (layerBuilder->CheckDOMModified()) michael@0: return; michael@0: michael@0: ThebesLayerItemsEntry* entry = layerBuilder->mThebesLayerItems.GetEntry(aLayer); michael@0: NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!"); michael@0: if (!entry->mContainerLayerFrame) { michael@0: return; michael@0: } michael@0: michael@0: michael@0: ThebesDisplayItemLayerUserData* userData = michael@0: static_cast michael@0: (aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); michael@0: NS_ASSERTION(userData, "where did our user data go?"); michael@0: michael@0: bool shouldDrawRectsSeparately = ShouldDrawRectsSeparately(aContext, aClip); michael@0: michael@0: if (!shouldDrawRectsSeparately) { michael@0: if (aClip == DrawRegionClip::DRAW_SNAPPED) { michael@0: gfxUtils::ClipToRegionSnapped(aContext, aRegionToDraw); michael@0: } else if (aClip == DrawRegionClip::DRAW) { michael@0: gfxUtils::ClipToRegion(aContext, aRegionToDraw); michael@0: } michael@0: michael@0: DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor); michael@0: } michael@0: michael@0: // make the origin of the context coincide with the origin of the michael@0: // ThebesLayer michael@0: gfxContextMatrixAutoSaveRestore saveMatrix(aContext); michael@0: nsIntPoint offset = GetTranslationForThebesLayer(aLayer); michael@0: michael@0: nsPresContext* presContext = entry->mContainerLayerFrame->PresContext(); michael@0: int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); michael@0: michael@0: RecomputeVisibilityForItems(entry->mItems, builder, aRegionToDraw, michael@0: offset, appUnitsPerDevPixel, michael@0: userData->mXScale, userData->mYScale); michael@0: michael@0: nsRefPtr rc = new nsRenderingContext(); michael@0: rc->Init(presContext->DeviceContext(), aContext); michael@0: michael@0: if (shouldDrawRectsSeparately) { michael@0: nsIntRegionRectIterator it(aRegionToDraw); michael@0: while (const nsIntRect* iterRect = it.Next()) { michael@0: gfxContextAutoSaveRestore save(aContext); michael@0: aContext->NewPath(); michael@0: aContext->Rectangle(*iterRect, aClip == DrawRegionClip::DRAW_SNAPPED); michael@0: aContext->Clip(); michael@0: michael@0: DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor); michael@0: michael@0: // Apply the residual transform if it has been enabled, to ensure that michael@0: // snapping when we draw into aContext exactly matches the ideal transform. michael@0: // See above for why this is OK. michael@0: aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)); michael@0: aContext->Scale(userData->mXScale, userData->mYScale); michael@0: michael@0: layerBuilder->PaintItems(entry->mItems, *iterRect, aContext, rc, michael@0: builder, presContext, michael@0: offset, userData->mXScale, userData->mYScale, michael@0: entry->mCommonClipCount); michael@0: } michael@0: } else { michael@0: // Apply the residual transform if it has been enabled, to ensure that michael@0: // snapping when we draw into aContext exactly matches the ideal transform. michael@0: // See above for why this is OK. michael@0: aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)); michael@0: aContext->Scale(userData->mXScale, userData->mYScale); michael@0: michael@0: layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext, rc, michael@0: builder, presContext, michael@0: offset, userData->mXScale, userData->mYScale, michael@0: entry->mCommonClipCount); michael@0: } michael@0: michael@0: if (presContext->GetPaintFlashing()) { michael@0: FlashPaint(aContext); michael@0: } michael@0: michael@0: if (!aRegionToInvalidate.IsEmpty()) { michael@0: aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds()); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: FrameLayerBuilder::CheckDOMModified() michael@0: { michael@0: if (!mRootPresContext || michael@0: mInitialDOMGeneration == mRootPresContext->GetDOMGeneration()) michael@0: return false; michael@0: if (mDetectedDOMModification) { michael@0: // Don't spam the console with extra warnings michael@0: return true; michael@0: } michael@0: mDetectedDOMModification = true; michael@0: // Painting is not going to complete properly. There's not much michael@0: // we can do here though. Invalidating the window to get another repaint michael@0: // is likely to lead to an infinite repaint loop. michael@0: NS_WARNING("Detected DOM modification during paint, bailing out!"); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_DUMP_PAINTING michael@0: /* static */ void michael@0: FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bool aDumpHtml) michael@0: { michael@0: aManager->Dump(aFile, "", aDumpHtml); michael@0: } michael@0: #endif michael@0: michael@0: gfx::Rect michael@0: CalculateBounds(const nsTArray& aRects, int32_t A2D) michael@0: { michael@0: nsRect bounds = aRects[0].mRect; michael@0: for (uint32_t i = 1; i < aRects.Length(); ++i) { michael@0: bounds.UnionRect(bounds, aRects[i].mRect); michael@0: } michael@0: michael@0: return gfx::ToRect(nsLayoutUtils::RectToGfxRect(bounds, A2D)); michael@0: } michael@0: michael@0: static void michael@0: SetClipCount(ThebesDisplayItemLayerUserData* aThebesData, michael@0: uint32_t aClipCount) michael@0: { michael@0: if (aThebesData) { michael@0: aThebesData->mMaskClipCount = aClipCount; michael@0: } michael@0: } michael@0: michael@0: void michael@0: ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip, michael@0: uint32_t aRoundedRectClipCount) michael@0: { michael@0: // if the number of clips we are going to mask has decreased, then aLayer might have michael@0: // cached graphics which assume the existence of a soon-to-be non-existent mask layer michael@0: // in that case, invalidate the whole layer. michael@0: ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer); michael@0: if (thebesData && michael@0: aRoundedRectClipCount < thebesData->mMaskClipCount) { michael@0: ThebesLayer* thebes = aLayer->AsThebesLayer(); michael@0: thebes->InvalidateRegion(thebes->GetValidRegion().GetBounds()); michael@0: } michael@0: michael@0: // don't build an unnecessary mask michael@0: nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds(); michael@0: if (aClip.GetRoundedRectCount() == 0 || michael@0: aRoundedRectClipCount == 0 || michael@0: layerBounds.IsEmpty()) { michael@0: SetClipCount(thebesData, 0); michael@0: return; michael@0: } michael@0: michael@0: // check if we can re-use the mask layer michael@0: nsRefPtr maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer); michael@0: MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer); michael@0: michael@0: MaskLayerUserData newData; michael@0: aClip.AppendRoundedRects(&newData.mRoundedClipRects, aRoundedRectClipCount); michael@0: newData.mScaleX = mParameters.mXScale; michael@0: newData.mScaleY = mParameters.mYScale; michael@0: newData.mOffset = mParameters.mOffset; michael@0: newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel(); michael@0: michael@0: if (*userData == newData) { michael@0: aLayer->SetMaskLayer(maskLayer); michael@0: SetClipCount(thebesData, aRoundedRectClipCount); michael@0: return; michael@0: } michael@0: michael@0: // calculate a more precise bounding rect michael@0: gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects, michael@0: newData.mAppUnitsPerDevPixel); michael@0: boundingRect.Scale(mParameters.mXScale, mParameters.mYScale); michael@0: michael@0: uint32_t maxSize = mManager->GetMaxTextureSize(); michael@0: NS_ASSERTION(maxSize > 0, "Invalid max texture size"); michael@0: gfx::Size surfaceSize(std::min(boundingRect.Width(), maxSize), michael@0: std::min(boundingRect.Height(), maxSize)); michael@0: michael@0: // maskTransform is applied to the clip when it is painted into the mask (as a michael@0: // component of imageTransform), and its inverse used when the mask is used for michael@0: // masking. michael@0: // It is the transform from the masked layer's space to mask space michael@0: gfx::Matrix maskTransform; michael@0: maskTransform.Scale(surfaceSize.width/boundingRect.Width(), michael@0: surfaceSize.height/boundingRect.Height()); michael@0: gfx::Point p = boundingRect.TopLeft(); michael@0: maskTransform.Translate(-p.x, -p.y); michael@0: // imageTransform is only used when the clip is painted to the mask michael@0: gfx::Matrix imageTransform = maskTransform; michael@0: imageTransform.Scale(mParameters.mXScale, mParameters.mYScale); michael@0: michael@0: nsAutoPtr newKey( michael@0: new MaskLayerImageCache::MaskLayerImageKey()); michael@0: michael@0: // copy and transform the rounded rects michael@0: for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) { michael@0: newKey->mRoundedClipRects.AppendElement( michael@0: MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i], michael@0: mContainerFrame->PresContext())); michael@0: newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform); michael@0: } michael@0: michael@0: const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey; michael@0: michael@0: // check to see if we can reuse a mask image michael@0: nsRefPtr container = michael@0: GetMaskLayerImageCache()->FindImageFor(&lookupKey); michael@0: michael@0: if (!container) { michael@0: IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width), michael@0: NSToIntCeil(surfaceSize.height)); michael@0: // no existing mask image, so build a new one michael@0: RefPtr dt = michael@0: aLayer->Manager()->CreateOptimalMaskDrawTarget(surfaceSizeInt); michael@0: michael@0: // fail if we can't get the right surface michael@0: if (!dt) { michael@0: NS_WARNING("Could not create DrawTarget for mask layer."); michael@0: SetClipCount(thebesData, 0); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr context = new gfxContext(dt); michael@0: context->Multiply(ThebesMatrix(imageTransform)); michael@0: michael@0: // paint the clipping rects with alpha to create the mask michael@0: context->SetColor(gfxRGBA(1, 1, 1, 1)); michael@0: aClip.DrawRoundedRectsTo(context, michael@0: newData.mAppUnitsPerDevPixel, michael@0: 0, michael@0: aRoundedRectClipCount); michael@0: michael@0: RefPtr surface = dt->Snapshot(); michael@0: michael@0: // build the image and container michael@0: container = aLayer->Manager()->CreateImageContainer(); michael@0: NS_ASSERTION(container, "Could not create image container for mask layer."); michael@0: nsRefPtr image = container->CreateImage(ImageFormat::CAIRO_SURFACE); michael@0: NS_ASSERTION(image, "Could not create image container for mask layer."); michael@0: CairoImage::Data data; michael@0: data.mSize = surfaceSizeInt; michael@0: data.mSourceSurface = surface; michael@0: michael@0: static_cast(image.get())->SetData(data); michael@0: container->SetCurrentImageInTransaction(image); michael@0: michael@0: GetMaskLayerImageCache()->PutImage(newKey.forget(), container); michael@0: } michael@0: michael@0: maskLayer->SetContainer(container); michael@0: michael@0: maskTransform.Invert(); michael@0: Matrix4x4 matrix = Matrix4x4::From2D(maskTransform); michael@0: matrix.Translate(mParameters.mOffset.x, mParameters.mOffset.y, 0); michael@0: maskLayer->SetBaseTransform(matrix); michael@0: michael@0: // save the details of the clip in user data michael@0: userData->mScaleX = newData.mScaleX; michael@0: userData->mScaleY = newData.mScaleY; michael@0: userData->mOffset = newData.mOffset; michael@0: userData->mAppUnitsPerDevPixel = newData.mAppUnitsPerDevPixel; michael@0: userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects); michael@0: userData->mImageKey = lookupKey; michael@0: michael@0: aLayer->SetMaskLayer(maskLayer); michael@0: SetClipCount(thebesData, aRoundedRectClipCount); michael@0: return; michael@0: } michael@0: michael@0: } // namespace mozilla