Bug 846320 - Remove SimpleTest.expectAssertions in test_seek.html. r=dbaron
[gecko.git] / layout / base / FrameLayerBuilder.cpp
blob8933ac0e44f57c19be6d7647d62b45d81714da1d
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/DebugOnly.h"
8 #include "FrameLayerBuilder.h"
10 #include "nsDisplayList.h"
11 #include "nsPresContext.h"
12 #include "nsLayoutUtils.h"
13 #include "Layers.h"
14 #include "BasicLayers.h"
15 #include "nsSubDocumentFrame.h"
16 #include "nsCSSRendering.h"
17 #include "nsCSSFrameConstructor.h"
18 #include "gfxUtils.h"
19 #include "nsRenderingContext.h"
20 #include "MaskLayerImageCache.h"
21 #include "nsIScrollableFrame.h"
22 #include "nsPrintfCString.h"
23 #include "LayerTreeInvalidation.h"
24 #include "nsSVGIntegrationUtils.h"
26 #include "mozilla/Preferences.h"
27 #include "sampler.h"
28 #include "mozilla/gfx/Tools.h"
30 #include "nsAnimationManager.h"
31 #include "nsTransitionManager.h"
32 #include <algorithm>
34 #ifdef DEBUG
35 #include <stdio.h>
36 //#define DEBUG_INVALIDATIONS
37 //#define DEBUG_DISPLAY_ITEM_DATA
38 #endif
40 using namespace mozilla::layers;
41 using namespace mozilla::gfx;
43 namespace mozilla {
45 FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey,
46 Layer* aLayer, LayerState aLayerState, uint32_t aGeneration)
48 : mParent(aParent)
49 , mLayer(aLayer)
50 , mDisplayItemKey(aKey)
51 , mContainerLayerGeneration(aGeneration)
52 , mLayerState(aLayerState)
53 , mUsed(true)
54 , mIsInvalid(false)
58 FrameLayerBuilder::DisplayItemData::DisplayItemData(DisplayItemData &toCopy)
60 // This isn't actually a copy-constructor; notice that it steals toCopy's
61 // mGeometry pointer. Be careful.
62 mParent = toCopy.mParent;
63 mLayer = toCopy.mLayer;
64 mInactiveManager = toCopy.mInactiveManager;
65 mFrameList = toCopy.mFrameList;
66 mGeometry = toCopy.mGeometry;
67 mDisplayItemKey = toCopy.mDisplayItemKey;
68 mClip = toCopy.mClip;
69 mContainerLayerGeneration = toCopy.mContainerLayerGeneration;
70 mLayerState = toCopy.mLayerState;
71 mUsed = toCopy.mUsed;
74 void
75 FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame)
77 mFrameList.AppendElement(aFrame);
79 nsTArray<DisplayItemData*> *array =
80 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty()));
81 if (!array) {
82 array = new nsTArray<DisplayItemData*>();
83 aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array);
85 array->AppendElement(this);
88 void
89 FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame)
91 DebugOnly<bool> result = mFrameList.RemoveElement(aFrame);
92 NS_ASSERTION(result, "Can't remove a frame that wasn't added!");
94 nsTArray<DisplayItemData*> *array =
95 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty()));
96 NS_ASSERTION(array, "Must be already stored on the frame!");
97 array->RemoveElement(this);
100 void
101 FrameLayerBuilder::DisplayItemData::UpdateContents(Layer* aLayer, LayerState aState,
102 uint32_t aContainerLayerGeneration,
103 nsDisplayItem* aItem /* = nullptr */)
105 mLayer = aLayer;
106 mOptLayer = nullptr;
107 mInactiveManager = nullptr;
108 mLayerState = aState;
109 mContainerLayerGeneration = aContainerLayerGeneration;
110 mGeometry = nullptr;
111 mClip.mHaveClipRect = false;
112 mClip.mRoundedClipRects.Clear();
113 mUsed = true;
115 if (!aItem) {
116 return;
119 nsAutoTArray<nsIFrame*, 4> copy(mFrameList);
120 if (!copy.RemoveElement(aItem->GetUnderlyingFrame())) {
121 AddFrame(aItem->GetUnderlyingFrame());
124 nsAutoTArray<nsIFrame*,4> mergedFrames;
125 aItem->GetMergedFrames(&mergedFrames);
126 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
127 if (!copy.RemoveElement(mergedFrames[i])) {
128 AddFrame(mergedFrames[i]);
132 for (uint32_t i = 0; i < copy.Length(); i++) {
133 RemoveFrame(copy[i]);
137 static nsIFrame* sDestroyedFrame = NULL;
138 FrameLayerBuilder::DisplayItemData::~DisplayItemData()
140 for (uint32_t i = 0; i < mFrameList.Length(); i++) {
141 nsIFrame* frame = mFrameList[i];
142 if (frame == sDestroyedFrame) {
143 continue;
145 nsTArray<DisplayItemData*> *array =
146 reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->Properties().Get(LayerManagerDataProperty()));
147 array->RemoveElement(this);
151 void
152 FrameLayerBuilder::DisplayItemData::GetFrameListChanges(nsDisplayItem* aOther,
153 nsTArray<nsIFrame*>& aOut)
155 aOut = mFrameList;
156 nsAutoTArray<nsIFrame*, 4> added;
157 if (!aOut.RemoveElement(aOther->GetUnderlyingFrame())) {
158 added.AppendElement(aOther->GetUnderlyingFrame());
161 nsAutoTArray<nsIFrame*,4> mergedFrames;
162 aOther->GetMergedFrames(&mergedFrames);
163 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
164 if (!aOut.RemoveElement(mergedFrames[i])) {
165 added.AppendElement(mergedFrames[i]);
169 aOut.AppendElements(added);
173 * This is the userdata we associate with a layer manager.
175 class LayerManagerData : public LayerUserData {
176 public:
177 LayerManagerData(LayerManager *aManager)
178 : mLayerManager(aManager)
179 #ifdef DEBUG_DISPLAY_ITEM_DATA
180 , mParent(nullptr)
181 #endif
182 , mInvalidateAllLayers(false)
184 MOZ_COUNT_CTOR(LayerManagerData);
185 mDisplayItems.Init();
187 ~LayerManagerData() {
188 MOZ_COUNT_DTOR(LayerManagerData);
191 #ifdef DEBUG_DISPLAY_ITEM_DATA
192 void Dump(const char *aPrefix = "") {
193 printf("%sLayerManagerData %p\n", aPrefix, this);
194 nsAutoCString prefix;
195 prefix += aPrefix;
196 prefix += " ";
197 mDisplayItems.EnumerateEntries(
198 FrameLayerBuilder::DumpDisplayItemDataForFrame, (void*)prefix.get());
200 #endif
203 * Tracks which frames have layers associated with them.
205 LayerManager *mLayerManager;
206 #ifdef DEBUG_DISPLAY_ITEM_DATA
207 LayerManagerData *mParent;
208 #endif
209 nsTHashtable<nsRefPtrHashKey<FrameLayerBuilder::DisplayItemData> > mDisplayItems;
210 bool mInvalidateAllLayers;
213 /* static */ void
214 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame)
216 FrameProperties props = aFrame->Properties();
217 props.Delete(LayerManagerDataProperty());
220 namespace {
222 // a global cache of image containers used for mask layers
223 static MaskLayerImageCache* gMaskLayerImageCache = nullptr;
225 static inline MaskLayerImageCache* GetMaskLayerImageCache()
227 if (!gMaskLayerImageCache) {
228 gMaskLayerImageCache = new MaskLayerImageCache();
231 return gMaskLayerImageCache;
235 * This is a helper object used to build up the layer children for
236 * a ContainerLayer.
238 class ContainerState {
239 public:
240 ContainerState(nsDisplayListBuilder* aBuilder,
241 LayerManager* aManager,
242 FrameLayerBuilder* aLayerBuilder,
243 nsIFrame* aContainerFrame,
244 nsDisplayItem* aContainerItem,
245 ContainerLayer* aContainerLayer,
246 const FrameLayerBuilder::ContainerParameters& aParameters) :
247 mBuilder(aBuilder), mManager(aManager),
248 mLayerBuilder(aLayerBuilder),
249 mContainerFrame(aContainerFrame),
250 mContainerLayer(aContainerLayer),
251 mParameters(aParameters),
252 mNextFreeRecycledThebesLayer(0)
254 nsPresContext* presContext = aContainerFrame->PresContext();
255 mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
256 mContainerReferenceFrame = aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
257 mBuilder->FindReferenceFrameFor(mContainerFrame);
258 // When AllowResidualTranslation is false, display items will be drawn
259 // scaled with a translation by integer pixels, so we know how the snapping
260 // will work.
261 mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() &&
262 !mParameters.AllowResidualTranslation();
263 mRecycledMaskImageLayers.Init();
264 CollectOldLayers();
267 enum ProcessDisplayItemsFlags {
268 NO_COMPONENT_ALPHA = 0x01,
272 * This is the method that actually walks a display list and builds
273 * the child layers. We invoke it recursively to process clipped sublists.
274 * @param aClipRect the clip rect to apply to the list items, or null
275 * if no clipping is required
277 void ProcessDisplayItems(const nsDisplayList& aList,
278 FrameLayerBuilder::Clip& aClip,
279 uint32_t aFlags,
280 const nsIFrame* aForceActiveScrolledRoot = nullptr);
282 * This finalizes all the open ThebesLayers by popping every element off
283 * mThebesLayerDataStack, then sets the children of the container layer
284 * to be all the layers in mNewChildLayers in that order and removes any
285 * layers as children of the container that aren't in mNewChildLayers.
286 * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA,
287 * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA
289 void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData);
291 nsRect GetChildrenBounds() { return mBounds; }
293 nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; }
295 nsIntRect ScaleToNearestPixels(const nsRect& aRect)
297 return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
298 mAppUnitsPerDevPixel);
300 nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion)
302 return aRegion.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale,
303 mAppUnitsPerDevPixel);
305 nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap)
307 if (aSnap && mSnappingEnabled) {
308 return ScaleToNearestPixels(aRect);
310 return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale,
311 mAppUnitsPerDevPixel);
313 nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap)
315 if (aSnap && mSnappingEnabled) {
316 return ScaleToNearestPixels(aRect);
318 return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
319 mAppUnitsPerDevPixel);
322 nsIntRegion ScaleRegionToInsidePixels(const nsRegion& aRegion, bool aSnap)
324 if (aSnap && mSnappingEnabled) {
325 return ScaleRegionToNearestPixels(aRegion);
327 return aRegion.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale,
328 mAppUnitsPerDevPixel);
331 protected:
333 * We keep a stack of these to represent the ThebesLayers that are
334 * currently available to have display items added to.
335 * We use a stack here because as much as possible we want to
336 * assign display items to existing ThebesLayers, and to the lowest
337 * ThebesLayer in z-order. This reduces the number of layers and
338 * makes it more likely a display item will be rendered to an opaque
339 * layer, giving us the best chance of getting subpixel AA.
341 class ThebesLayerData {
342 public:
343 ThebesLayerData() :
344 mActiveScrolledRoot(nullptr), mLayer(nullptr),
345 mIsSolidColorInVisibleRegion(false),
346 mNeedComponentAlpha(false),
347 mForceTransparentSurface(false),
348 mImage(nullptr),
349 mCommonClipCount(-1) {}
351 * Record that an item has been added to the ThebesLayer, so we
352 * need to update our regions.
353 * @param aVisibleRect the area of the item that's visible
354 * @param aDrawRect the area of the item that would be drawn if it
355 * was completely visible
356 * @param aOpaqueRect if non-null, the area of the item that's opaque.
357 * We pass in a separate opaque rect because the opaque rect can be
358 * bigger than the visible rect, and we want to have the biggest
359 * opaque rect that we can.
360 * @param aSolidColor if non-null, the visible area of the item is
361 * a constant color given by *aSolidColor
363 void Accumulate(ContainerState* aState,
364 nsDisplayItem* aItem,
365 const nsIntRect& aVisibleRect,
366 const nsIntRect& aDrawRect,
367 const FrameLayerBuilder::Clip& aClip);
368 const nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; }
371 * If this represents only a nsDisplayImage, and the image type
372 * supports being optimized to an ImageLayer (TYPE_RASTER only) returns
373 * an ImageContainer for the image.
375 already_AddRefed<ImageContainer> CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder);
378 * The region of visible content in the layer, relative to the
379 * container layer (which is at the snapped top-left of the display
380 * list reference frame).
382 nsIntRegion mVisibleRegion;
384 * The region of visible content above the layer and below the
385 * next ThebesLayerData currently in the stack, if any. Note that not
386 * all ThebesLayers for the container are in the ThebesLayerData stack.
387 * Same coordinate system as mVisibleRegion.
388 * This is a conservative approximation: it contains the true region.
390 nsIntRegion mVisibleAboveRegion;
392 * The region containing the bounds of all display items in the layer,
393 * regardless of visbility.
394 * Same coordinate system as mVisibleRegion.
395 * This is a conservative approximation: it contains the true region.
397 nsIntRegion mDrawRegion;
399 * The region containing the bounds of all display items (regardless
400 * of visibility) in the layer and below the next ThebesLayerData
401 * currently in the stack, if any.
402 * Note that not all ThebesLayers for the container are in the
403 * ThebesLayerData stack.
404 * Same coordinate system as mVisibleRegion.
406 nsIntRegion mDrawAboveRegion;
408 * The region of visible content in the layer that is opaque.
409 * Same coordinate system as mVisibleRegion.
411 nsIntRegion mOpaqueRegion;
413 * The "active scrolled root" for all content in the layer. Must
414 * be non-null; all content in a ThebesLayer must have the same
415 * active scrolled root.
417 const nsIFrame* mActiveScrolledRoot;
418 ThebesLayer* mLayer;
420 * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
421 * region.
423 nscolor mSolidColor;
425 * True if every pixel in mVisibleRegion will have color mSolidColor.
427 bool mIsSolidColorInVisibleRegion;
429 * True if there is any text visible in the layer that's over
430 * transparent pixels in the layer.
432 bool mNeedComponentAlpha;
434 * Set if the layer should be treated as transparent, even if its entire
435 * area is covered by opaque display items. For example, this needs to
436 * be set if something is going to "punch holes" in the layer by clearing
437 * part of its surface.
439 bool mForceTransparentSurface;
442 * Stores the pointer to the nsDisplayImage if we want to
443 * convert this to an ImageLayer.
445 nsDisplayImageContainer* mImage;
447 * Stores the clip that we need to apply to the image or, if there is no
448 * image, a clip for SOME item in the layer. There is no guarantee which
449 * item's clip will be stored here and mItemClip should not be used to clip
450 * the whole layer - only some part of the clip should be used, as determined
451 * by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be
452 * no part at all.
454 FrameLayerBuilder::Clip mItemClip;
456 * The first mCommonClipCount rounded rectangle clips are identical for
457 * all items in the layer.
458 * -1 if there are no items in the layer; must be >=0 by the time that this
459 * data is popped from the stack.
461 int32_t mCommonClipCount;
463 * Updates mCommonClipCount by checking for rounded rect clips in common
464 * between the clip on a new item (aCurrentClip) and the common clips
465 * on items already in the layer (the first mCommonClipCount rounded rects
466 * in mItemClip).
468 void UpdateCommonClipCount(const FrameLayerBuilder::Clip& aCurrentClip);
470 friend class ThebesLayerData;
473 * Grab the next recyclable ThebesLayer, or create one if there are no
474 * more recyclable ThebesLayers. Does any necessary invalidation of
475 * a recycled ThebesLayer, and sets up the transform on the ThebesLayer
476 * to account for scrolling.
478 already_AddRefed<ThebesLayer> CreateOrRecycleThebesLayer(const nsIFrame* aActiveScrolledRoot,
479 const nsIFrame *aReferenceFrame,
480 const nsPoint& aTopLeft);
482 * Grab the next recyclable ColorLayer, or create one if there are no
483 * more recyclable ColorLayers.
485 already_AddRefed<ColorLayer> CreateOrRecycleColorLayer(ThebesLayer* aThebes);
487 * Grab the next recyclable ImageLayer, or create one if there are no
488 * more recyclable ImageLayers.
490 already_AddRefed<ImageLayer> CreateOrRecycleImageLayer(ThebesLayer* aThebes);
492 * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a
493 * mask layer which has been used for aLayer before), or create one if such
494 * a layer doesn't exist.
496 already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(Layer* aLayer);
498 * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them
499 * available for recycling.
501 void CollectOldLayers();
503 * If aItem used to belong to a ThebesLayer, invalidates the area of
504 * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of
505 * aItem in that layer.
507 void InvalidateForLayerChange(nsDisplayItem* aItem,
508 Layer* aNewLayer,
509 const FrameLayerBuilder::Clip& aClip,
510 const nsPoint& aTopLeft,
511 nsDisplayItemGeometry *aGeometry);
513 * Try to determine whether the ThebesLayer at aThebesLayerIndex
514 * has a single opaque color behind it, over the entire bounds of its visible
515 * region.
516 * If successful, return that color, otherwise return NS_RGBA(0,0,0,0).
518 nscolor FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex);
520 * Indicate that we are done adding items to the ThebesLayer at the top of
521 * mThebesLayerDataStack. Set the final visible region and opaque-content
522 * flag, and pop it off the stack.
524 void PopThebesLayerData();
526 * Find the ThebesLayer to which we should assign the next display item.
527 * We scan the ThebesLayerData stack to find the topmost ThebesLayer
528 * that is compatible with the display item (i.e., has the same
529 * active scrolled root), and that has no content from other layers above
530 * it and intersecting the aVisibleRect.
531 * Returns the layer, and also updates the ThebesLayerData. Will
532 * push a new ThebesLayerData onto the stack if no suitable existing
533 * layer is found. If we choose a ThebesLayer that's already on the
534 * ThebesLayerData stack, later elements on the stack will be popped off.
535 * @param aVisibleRect the area of the next display item that's visible
536 * @param aActiveScrolledRoot the active scrolled root for the next
537 * display item
538 * @param aOpaqueRect if non-null, a region of the display item that is opaque
539 * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect
540 * will be painted with aSolidColor by the item
542 ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem,
543 const nsIntRect& aVisibleRect,
544 const nsIntRect& aDrawRect,
545 const FrameLayerBuilder::Clip& aClip,
546 const nsIFrame* aActiveScrolledRoot,
547 const nsPoint& aTopLeft);
548 ThebesLayerData* GetTopThebesLayerData()
550 return mThebesLayerDataStack.IsEmpty() ? nullptr
551 : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get();
554 /* Build a mask layer to represent the clipping region. Will return null if
555 * there is no clipping specified or a mask layer cannot be built.
556 * Builds an ImageLayer for the appropriate backend; the mask is relative to
557 * aLayer's visible region.
558 * aLayer is the layer to be clipped.
559 * aRoundedRectClipCount is used when building mask layers for ThebesLayers,
560 * SetupMaskLayer will build a mask layer for only the first
561 * aRoundedRectClipCount rounded rects in aClip
563 void SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
564 uint32_t aRoundedRectClipCount = UINT32_MAX);
566 bool ChooseActiveScrolledRoot(const nsDisplayList& aList,
567 const nsIFrame **aActiveScrolledRoot);
569 nsDisplayListBuilder* mBuilder;
570 LayerManager* mManager;
571 FrameLayerBuilder* mLayerBuilder;
572 nsIFrame* mContainerFrame;
573 const nsIFrame* mContainerReferenceFrame;
574 ContainerLayer* mContainerLayer;
575 FrameLayerBuilder::ContainerParameters mParameters;
577 * The region of ThebesLayers that should be invalidated every time
578 * we recycle one.
580 nsIntRegion mInvalidThebesContent;
581 nsRect mBounds;
582 nsAutoTArray<nsAutoPtr<ThebesLayerData>,1> mThebesLayerDataStack;
584 * We collect the list of children in here. During ProcessDisplayItems,
585 * the layers in this array either have mContainerLayer as their parent,
586 * or no parent.
588 typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray;
589 AutoLayersArray mNewChildLayers;
590 nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers;
591 nsDataHashtable<nsPtrHashKey<Layer>, nsRefPtr<ImageLayer> >
592 mRecycledMaskImageLayers;
593 uint32_t mNextFreeRecycledThebesLayer;
594 nscoord mAppUnitsPerDevPixel;
595 bool mSnappingEnabled;
598 class ThebesDisplayItemLayerUserData : public LayerUserData
600 public:
601 ThebesDisplayItemLayerUserData() :
602 mMaskClipCount(0),
603 mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
604 mXScale(1.f), mYScale(1.f),
605 mAppUnitsPerDevPixel(0),
606 mTranslation(0, 0),
607 mActiveScrolledRootPosition(0, 0) {}
610 * Record the number of clips in the Thebes layer's mask layer.
611 * Should not be reset when the layer is recycled since it is used to track
612 * changes in the use of mask layers.
614 uint32_t mMaskClipCount;
617 * A color that should be painted over the bounds of the layer's visible
618 * region before any other content is painted.
620 nscolor mForcedBackgroundColor;
623 * The resolution scale used.
625 float mXScale, mYScale;
628 * The appunits per dev pixel for the items in this layer.
630 nscoord mAppUnitsPerDevPixel;
633 * The offset from the ThebesLayer's 0,0 to the
634 * reference frame. This isn't necessarily the same as the transform
635 * set on the ThebesLayer since we might also be applying an extra
636 * offset specified by the parent ContainerLayer/
638 nsIntPoint mTranslation;
641 * We try to make 0,0 of the ThebesLayer be the top-left of the
642 * border-box of the "active scrolled root" frame (i.e. the nearest ancestor
643 * frame for the display items that is being actively scrolled). But
644 * we force the ThebesLayer transform to be an integer translation, and we may
645 * have a resolution scale, so we have to snap the ThebesLayer transform, so
646 * 0,0 may not be exactly the top-left of the active scrolled root. Here we
647 * store the coordinates in ThebesLayer space of the top-left of the
648 * active scrolled root.
650 gfxPoint mActiveScrolledRootPosition;
652 nsIntRegion mRegionToInvalidate;
654 // The offset between the active scrolled root of this layer
655 // and the root of the container for the previous and current
656 // paints respectively.
657 nsPoint mLastActiveScrolledRootOrigin;
658 nsPoint mActiveScrolledRootOrigin;
660 nsRefPtr<ColorLayer> mColorLayer;
661 nsRefPtr<ImageLayer> mImageLayer;
665 * User data for layers which will be used as masks.
667 struct MaskLayerUserData : public LayerUserData
669 MaskLayerUserData() : mImageKey(nullptr) {}
671 bool
672 operator== (const MaskLayerUserData& aOther) const
674 return mRoundedClipRects == aOther.mRoundedClipRects &&
675 mScaleX == aOther.mScaleX &&
676 mScaleY == aOther.mScaleY;
679 nsRefPtr<const MaskLayerImageCache::MaskLayerImageKey> mImageKey;
680 // properties of the mask layer; the mask layer may be re-used if these
681 // remain unchanged.
682 nsTArray<FrameLayerBuilder::Clip::RoundedRect> mRoundedClipRects;
683 // scale from the masked layer which is applied to the mask
684 float mScaleX, mScaleY;
688 * The address of gThebesDisplayItemLayerUserData is used as the user
689 * data key for ThebesLayers created by FrameLayerBuilder.
690 * It identifies ThebesLayers used to draw non-layer content, which are
691 * therefore eligible for recycling. We want display items to be able to
692 * create their own dedicated ThebesLayers in BuildLayer, if necessary,
693 * and we wouldn't want to accidentally recycle those.
694 * The user data is a ThebesDisplayItemLayerUserData.
696 uint8_t gThebesDisplayItemLayerUserData;
698 * The address of gColorLayerUserData is used as the user
699 * data key for ColorLayers created by FrameLayerBuilder.
700 * The user data is null.
702 uint8_t gColorLayerUserData;
704 * The address of gImageLayerUserData is used as the user
705 * data key for ImageLayers created by FrameLayerBuilder.
706 * The user data is null.
708 uint8_t gImageLayerUserData;
710 * The address of gLayerManagerUserData is used as the user
711 * data key for retained LayerManagers managed by FrameLayerBuilder.
712 * The user data is a LayerManagerData.
714 uint8_t gLayerManagerUserData;
716 * The address of gMaskLayerUserData is used as the user
717 * data key for mask layers managed by FrameLayerBuilder.
718 * The user data is a MaskLayerUserData.
720 uint8_t gMaskLayerUserData;
723 * Helper functions for getting user data and casting it to the correct type.
724 * aLayer is the layer where the user data is stored.
726 MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer)
728 return static_cast<MaskLayerUserData*>(aLayer->GetUserData(&gMaskLayerUserData));
731 ThebesDisplayItemLayerUserData* GetThebesDisplayItemLayerUserData(Layer* aLayer)
733 return static_cast<ThebesDisplayItemLayerUserData*>(
734 aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
737 } // anonymous namespace
739 /* static */ void
740 FrameLayerBuilder::Shutdown()
742 if (gMaskLayerImageCache) {
743 delete gMaskLayerImageCache;
744 gMaskLayerImageCache = nullptr;
748 void
749 FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager)
751 mDisplayListBuilder = aBuilder;
752 mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext();
753 if (mRootPresContext) {
754 mInitialDOMGeneration = mRootPresContext->GetDOMGeneration();
756 aManager->SetUserData(&gLayerManagerLayerBuilder, this);
759 void
760 FrameLayerBuilder::FlashPaint(gfxContext *aContext)
762 static bool sPaintFlashingEnabled;
763 static bool sPaintFlashingPrefCached = false;
765 if (!sPaintFlashingPrefCached) {
766 sPaintFlashingPrefCached = true;
767 mozilla::Preferences::AddBoolVarCache(&sPaintFlashingEnabled,
768 "nglayout.debug.paint_flashing");
771 if (sPaintFlashingEnabled) {
772 float r = float(rand()) / RAND_MAX;
773 float g = float(rand()) / RAND_MAX;
774 float b = float(rand()) / RAND_MAX;
775 aContext->SetColor(gfxRGBA(r, g, b, 0.2));
776 aContext->Paint();
780 FrameLayerBuilder::DisplayItemData*
781 FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey)
783 nsTArray<DisplayItemData*> *array =
784 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
785 if (array) {
786 for (uint32_t i = 0; i < array->Length(); i++) {
787 DisplayItemData* item = array->ElementAt(i);
788 if (item->mDisplayItemKey == aKey &&
789 item->mLayer->Manager() == mRetainingManager) {
790 return item;
794 return nullptr;
797 nsACString&
798 AppendToString(nsACString& s, const nsIntRect& r,
799 const char* pfx="", const char* sfx="")
801 s += pfx;
802 s += nsPrintfCString(
803 "(x=%d, y=%d, w=%d, h=%d)",
804 r.x, r.y, r.width, r.height);
805 return s += sfx;
808 nsACString&
809 AppendToString(nsACString& s, const nsIntRegion& r,
810 const char* pfx="", const char* sfx="")
812 s += pfx;
814 nsIntRegionRectIterator it(r);
815 s += "< ";
816 while (const nsIntRect* sr = it.Next()) {
817 AppendToString(s, *sr) += "; ";
819 s += ">";
821 return s += sfx;
825 * Invalidate aRegion in aLayer. aLayer is in the coordinate system
826 * *after* aTranslation has been applied, so we need to
827 * apply the inverse of that transform before calling InvalidateRegion.
829 static void
830 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion,
831 const nsIntPoint& aTranslation)
833 // Convert the region from the coordinates of the container layer
834 // (relative to the snapped top-left of the display list reference frame)
835 // to the ThebesLayer's own coordinates
836 nsIntRegion rgn = aRegion;
837 rgn.MoveBy(-aTranslation);
838 aLayer->InvalidateRegion(rgn);
839 #ifdef DEBUG_INVALIDATIONS
840 nsAutoCString str;
841 AppendToString(str, rgn);
842 printf("Invalidating layer %p: %s\n", aLayer, str.get());
843 #endif
846 static void
847 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect,
848 const FrameLayerBuilder::Clip& aClip,
849 const nsIntPoint& aTranslation)
851 ThebesDisplayItemLayerUserData* data =
852 static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
854 nsRect rect = aClip.ApplyNonRoundedIntersection(aRect);
856 nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel);
857 InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation);
861 static nsIntPoint
862 GetTranslationForThebesLayer(ThebesLayer* aLayer)
864 ThebesDisplayItemLayerUserData* data =
865 static_cast<ThebesDisplayItemLayerUserData*>
866 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
867 NS_ASSERTION(data, "Must be a tracked thebes layer!");
869 return data->mTranslation;
873 * Some frames can have multiple, nested, retaining layer managers
874 * associated with them (normal manager, inactive managers, SVG effects).
875 * In these cases we store the 'outermost' LayerManager data property
876 * on the frame since we can walk down the chain from there.
878 * If one of these frames has just been destroyed, we will free the inner
879 * layer manager when removing the entry from mFramesWithLayers. Destroying
880 * the layer manager destroys the LayerManagerData and calls into
881 * the DisplayItemData destructor. If the inner layer manager had any
882 * items with the same frame, then we attempt to retrieve properties
883 * from the deleted frame.
885 * Cache the destroyed frame pointer here so we can avoid crashing in this case.
888 /* static */ void
889 FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame,
890 void* aPropertyValue)
892 sDestroyedFrame = aFrame;
893 nsTArray<DisplayItemData*> *array =
894 reinterpret_cast<nsTArray<DisplayItemData*>*>(aPropertyValue);
896 // Hold a reference to all the items so that they don't get
897 // deleted from under us.
898 nsTArray<nsRefPtr<DisplayItemData> > arrayCopy;
899 for (uint32_t i = 0; i < array->Length(); ++i) {
900 arrayCopy.AppendElement(array->ElementAt(i));
903 #ifdef DEBUG_DISPLAY_ITEM_DATA
904 if (array->Length()) {
905 LayerManagerData *rootData = array->ElementAt(0)->mParent;
906 while (rootData->mParent) {
907 rootData = rootData->mParent;
909 printf("Removing frame %p - dumping display data\n", aFrame);
910 rootData->Dump();
912 #endif
914 for (uint32_t i = 0; i < array->Length(); ++i) {
915 DisplayItemData* data = array->ElementAt(i);
917 ThebesLayer* t = data->mLayer->AsThebesLayer();
918 if (t) {
919 ThebesDisplayItemLayerUserData* thebesData =
920 static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData));
921 if (thebesData) {
922 nsRegion old = data->mGeometry->ComputeInvalidationRegion();
923 nsIntRegion rgn = old.ScaleToOutsidePixels(thebesData->mXScale, thebesData->mYScale, thebesData->mAppUnitsPerDevPixel);
924 rgn.MoveBy(-GetTranslationForThebesLayer(t));
925 thebesData->mRegionToInvalidate.Or(thebesData->mRegionToInvalidate, rgn);
929 data->mParent->mDisplayItems.RemoveEntry(data);
932 arrayCopy.Clear();
933 delete array;
934 sDestroyedFrame = NULL;
937 void
938 FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager)
940 mRetainingManager = aManager;
941 LayerManagerData* data = static_cast<LayerManagerData*>
942 (aManager->GetUserData(&gLayerManagerUserData));
943 if (data) {
944 mInvalidateAllLayers = data->mInvalidateAllLayers;
945 } else {
946 data = new LayerManagerData(aManager);
947 aManager->SetUserData(&gLayerManagerUserData, data);
951 void
952 FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer)
954 if (!mRetainingManager) {
955 return;
958 DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager());
959 NS_ASSERTION(data, "Must have already stored data for this item!");
960 data->mOptLayer = aLayer;
963 void
964 FrameLayerBuilder::DidEndTransaction()
966 GetMaskLayerImageCache()->Sweep();
969 void
970 FrameLayerBuilder::WillEndTransaction()
972 if (!mRetainingManager) {
973 return;
976 // We need to save the data we'll need to support retaining.
977 LayerManagerData* data = static_cast<LayerManagerData*>
978 (mRetainingManager->GetUserData(&gLayerManagerUserData));
979 NS_ASSERTION(data, "Must have data!");
980 // Update all the frames that used to have layers.
981 data->mDisplayItems.EnumerateEntries(ProcessRemovedDisplayItems, this);
982 data->mInvalidateAllLayers = false;
985 /* static */ PLDHashOperator
986 FrameLayerBuilder::ProcessRemovedDisplayItems(nsRefPtrHashKey<DisplayItemData>* aEntry,
987 void* aUserArg)
989 DisplayItemData* data = aEntry->GetKey();
990 if (!data->mUsed) {
991 // This item was visible, but isn't anymore.
992 FrameLayerBuilder* layerBuilder = static_cast<FrameLayerBuilder*>(aUserArg);
994 ThebesLayer* t = data->mLayer->AsThebesLayer();
995 if (t) {
996 #ifdef DEBUG_INVALIDATIONS
997 printf("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t);
998 #endif
999 InvalidatePostTransformRegion(t,
1000 data->mGeometry->ComputeInvalidationRegion(),
1001 data->mClip,
1002 layerBuilder->GetLastPaintOffset(t));
1004 return PL_DHASH_REMOVE;
1007 data->mUsed = false;
1008 data->mIsInvalid = false;
1009 return PL_DHASH_NEXT;
1012 /* static */ PLDHashOperator
1013 FrameLayerBuilder::DumpDisplayItemDataForFrame(nsRefPtrHashKey<DisplayItemData>* aEntry,
1014 void* aClosure)
1016 #ifdef DEBUG_DISPLAY_ITEM_DATA
1017 DisplayItemData *data = aEntry->GetKey();
1019 nsAutoCString prefix;
1020 prefix += static_cast<const char*>(aClosure);
1022 const char *layerState;
1023 switch (data->mLayerState) {
1024 case LAYER_NONE:
1025 layerState = "LAYER_NONE"; break;
1026 case LAYER_INACTIVE:
1027 layerState = "LAYER_INACTIVE"; break;
1028 case LAYER_ACTIVE:
1029 layerState = "LAYER_ACTIVE"; break;
1030 case LAYER_ACTIVE_FORCE:
1031 layerState = "LAYER_ACTIVE_FORCE"; break;
1032 case LAYER_ACTIVE_EMPTY:
1033 layerState = "LAYER_ACTIVE_EMPTY"; break;
1034 case LAYER_SVG_EFFECTS:
1035 layerState = "LAYER_SVG_EFFECTS"; break;
1037 uint32_t mask = (1 << nsDisplayItem::TYPE_BITS) - 1;
1039 nsAutoCString str;
1040 str += prefix;
1041 str += nsPrintfCString("Frame %p ", data->mFrameList[0]);
1042 str += nsDisplayItem::DisplayItemTypeName(static_cast<nsDisplayItem::Type>(data->mDisplayItemKey & mask));
1043 if ((data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS)) {
1044 str += nsPrintfCString("(%i)", data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS);
1046 str += nsPrintfCString(", %s, Layer %p", layerState, data->mLayer.get());
1047 if (data->mOptLayer) {
1048 str += nsPrintfCString(", OptLayer %p", data->mOptLayer.get());
1050 if (data->mInactiveManager) {
1051 str += nsPrintfCString(", InactiveLayerManager %p", data->mInactiveManager.get());
1053 str += "\n";
1055 printf("%s", str.get());
1057 if (data->mInactiveManager) {
1058 prefix += " ";
1059 printf("%sDumping inactive layer info:\n", prefix.get());
1060 LayerManagerData* lmd = static_cast<LayerManagerData*>
1061 (data->mInactiveManager->GetUserData(&gLayerManagerUserData));
1062 lmd->Dump(prefix.get());
1064 #endif
1065 return PL_DHASH_NEXT;
1068 /* static */ FrameLayerBuilder::DisplayItemData*
1069 FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem,
1070 LayerManager* aManager)
1072 nsTArray<DisplayItemData*> *array =
1073 reinterpret_cast<nsTArray<DisplayItemData*>*>(aItem->GetUnderlyingFrame()->Properties().Get(LayerManagerDataProperty()));
1074 if (array) {
1075 for (uint32_t i = 0; i < array->Length(); i++) {
1076 DisplayItemData* item = array->ElementAt(i);
1077 if (item->mDisplayItemKey == aItem->GetPerFrameKey() &&
1078 item->mLayer->Manager() == aManager) {
1079 return item;
1083 return nullptr;
1086 bool
1087 FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1089 nsTArray<DisplayItemData*> *array =
1090 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1091 if (array) {
1092 for (uint32_t i = 0; i < array->Length(); i++) {
1093 if (array->ElementAt(i)->mDisplayItemKey == aDisplayItemKey) {
1094 return true;
1098 return false;
1101 void
1102 FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback)
1104 nsTArray<DisplayItemData*> *array =
1105 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1106 if (!array) {
1107 return;
1110 for (uint32_t i = 0; i < array->Length(); i++) {
1111 DisplayItemData* data = array->ElementAt(i);
1112 if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) {
1113 aCallback(aFrame, data);
1118 FrameLayerBuilder::DisplayItemData*
1119 FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1121 // If we need to build a new layer tree, then just refuse to recycle
1122 // anything.
1123 if (!mRetainingManager || mInvalidateAllLayers)
1124 return nullptr;
1126 DisplayItemData *data = GetDisplayItemData(aFrame, aDisplayItemKey);
1128 if (data && data->mLayer->Manager() == mRetainingManager) {
1129 return data;
1131 return nullptr;
1134 Layer*
1135 FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem,
1136 nsDisplayItemGeometry** aOldGeometry,
1137 Clip** aOldClip,
1138 nsTArray<nsIFrame*>* aChangedFrames,
1139 bool *aIsInvalid)
1141 uint32_t key = aItem->GetPerFrameKey();
1142 nsIFrame* frame = aItem->GetUnderlyingFrame();
1144 if (frame) {
1145 DisplayItemData* oldData = GetOldLayerForFrame(frame, key);
1146 if (oldData) {
1147 if (aOldGeometry) {
1148 *aOldGeometry = oldData->mGeometry.get();
1150 if (aOldClip) {
1151 *aOldClip = &oldData->mClip;
1153 if (aChangedFrames) {
1154 oldData->GetFrameListChanges(aItem, *aChangedFrames);
1156 if (aIsInvalid) {
1157 *aIsInvalid = oldData->mIsInvalid;
1159 return oldData->mLayer;
1163 return nullptr;
1166 /* static */ Layer*
1167 FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey)
1169 nsTArray<DisplayItemData*> *array =
1170 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
1172 if (!array) {
1173 return nullptr;
1176 for (uint32_t i = 0; i < array->Length(); i++) {
1177 DisplayItemData *data = array->ElementAt(i);
1179 if (data->mDisplayItemKey == aDisplayItemKey) {
1180 return data->mLayer;
1183 return nullptr;
1186 already_AddRefed<ColorLayer>
1187 ContainerState::CreateOrRecycleColorLayer(ThebesLayer *aThebes)
1189 ThebesDisplayItemLayerUserData* data =
1190 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData));
1191 nsRefPtr<ColorLayer> layer = data->mColorLayer;
1192 if (layer) {
1193 layer->SetClipRect(nullptr);
1194 layer->SetMaskLayer(nullptr);
1195 } else {
1196 // Create a new layer
1197 layer = mManager->CreateColorLayer();
1198 if (!layer)
1199 return nullptr;
1200 // Mark this layer as being used for Thebes-painting display items
1201 data->mColorLayer = layer;
1202 layer->SetUserData(&gColorLayerUserData, nullptr);
1204 // Remove other layer types we might have stored for this ThebesLayer
1205 data->mImageLayer = nullptr;
1207 return layer.forget();
1210 already_AddRefed<ImageLayer>
1211 ContainerState::CreateOrRecycleImageLayer(ThebesLayer *aThebes)
1213 ThebesDisplayItemLayerUserData* data =
1214 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData));
1215 nsRefPtr<ImageLayer> layer = data->mImageLayer;
1216 if (layer) {
1217 layer->SetClipRect(nullptr);
1218 layer->SetMaskLayer(nullptr);
1219 } else {
1220 // Create a new layer
1221 layer = mManager->CreateImageLayer();
1222 if (!layer)
1223 return nullptr;
1224 // Mark this layer as being used for Thebes-painting display items
1225 data->mImageLayer = layer;
1226 layer->SetUserData(&gImageLayerUserData, nullptr);
1228 // Remove other layer types we might have stored for this ThebesLayer
1229 data->mColorLayer = nullptr;
1231 return layer.forget();
1234 already_AddRefed<ImageLayer>
1235 ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer)
1237 nsRefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aLayer);
1238 if (result) {
1239 mRecycledMaskImageLayers.Remove(aLayer);
1240 // XXX if we use clip on mask layers, null it out here
1241 } else {
1242 // Create a new layer
1243 result = mManager->CreateImageLayer();
1244 if (!result)
1245 return nullptr;
1246 result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData());
1247 result->SetForceSingleTile(true);
1250 return result.forget();
1253 static const double SUBPIXEL_OFFSET_EPSILON = 0.02;
1256 * This normally computes NSToIntRoundUp(aValue). However, if that would
1257 * give a residual near 0.5 while aOldResidual is near -0.5, or
1258 * it would give a residual near -0.5 while aOldResidual is near 0.5, then
1259 * instead we return the integer in the other direction so that the residual
1260 * is close to aOldResidual.
1262 static int32_t
1263 RoundToMatchResidual(double aValue, double aOldResidual)
1265 int32_t v = NSToIntRoundUp(aValue);
1266 double residual = aValue - v;
1267 if (aOldResidual < 0) {
1268 if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
1269 // Round up instead
1270 return int32_t(ceil(aValue));
1272 } else if (aOldResidual > 0) {
1273 if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) {
1274 // Round down instead
1275 return int32_t(floor(aValue));
1278 return v;
1281 static void
1282 ResetScrollPositionForLayerPixelAlignment(const nsIFrame* aActiveScrolledRoot)
1284 nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(aActiveScrolledRoot);
1285 if (sf) {
1286 sf->ResetScrollPositionForLayerPixelAlignment();
1290 static void
1291 InvalidateEntireThebesLayer(ThebesLayer* aLayer, const nsIFrame* aActiveScrolledRoot)
1293 #ifdef DEBUG_INVALIDATIONS
1294 printf("Invalidating entire layer %p\n", aLayer);
1295 #endif
1296 nsIntRect invalidate = aLayer->GetValidRegion().GetBounds();
1297 aLayer->InvalidateRegion(invalidate);
1298 ResetScrollPositionForLayerPixelAlignment(aActiveScrolledRoot);
1301 already_AddRefed<ThebesLayer>
1302 ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aActiveScrolledRoot,
1303 const nsIFrame* aReferenceFrame,
1304 const nsPoint& aTopLeft)
1306 // We need a new thebes layer
1307 nsRefPtr<ThebesLayer> layer;
1308 ThebesDisplayItemLayerUserData* data;
1309 #ifndef MOZ_ANDROID_OMTC
1310 bool didResetScrollPositionForLayerPixelAlignment = false;
1311 #endif
1312 if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) {
1313 // Recycle a layer
1314 layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer];
1315 ++mNextFreeRecycledThebesLayer;
1316 // Clear clip rect and mask layer so we don't accidentally stay clipped.
1317 // We will reapply any necessary clipping.
1318 layer->SetClipRect(nullptr);
1319 layer->SetMaskLayer(nullptr);
1321 data = static_cast<ThebesDisplayItemLayerUserData*>
1322 (layer->GetUserData(&gThebesDisplayItemLayerUserData));
1323 NS_ASSERTION(data, "Recycled ThebesLayers must have user data");
1325 // This gets called on recycled ThebesLayers that are going to be in the
1326 // final layer tree, so it's a convenient time to invalidate the
1327 // content that changed where we don't know what ThebesLayer it belonged
1328 // to, or if we need to invalidate the entire layer, we can do that.
1329 // This needs to be done before we update the ThebesLayer to its new
1330 // transform. See nsGfxScrollFrame::InvalidateInternal, where
1331 // we ensure that mInvalidThebesContent is updated according to the
1332 // scroll position as of the most recent paint.
1333 if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001) ||
1334 !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001) ||
1335 data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) {
1336 InvalidateEntireThebesLayer(layer, aActiveScrolledRoot);
1337 #ifndef MOZ_ANDROID_OMTC
1338 didResetScrollPositionForLayerPixelAlignment = true;
1339 #endif
1341 if (!data->mRegionToInvalidate.IsEmpty()) {
1342 #ifdef DEBUG_INVALIDATIONS
1343 printf("Invalidating deleted frame content from layer %p\n", layer.get());
1344 #endif
1345 layer->InvalidateRegion(data->mRegionToInvalidate);
1346 #ifdef DEBUG_INVALIDATIONS
1347 nsAutoCString str;
1348 AppendToString(str, data->mRegionToInvalidate);
1349 printf("Invalidating layer %p: %s\n", layer.get(), str.get());
1350 #endif
1351 data->mRegionToInvalidate.SetEmpty();
1354 // We do not need to Invalidate these areas in the widget because we
1355 // assume the caller of InvalidateThebesLayerContents has ensured
1356 // the area is invalidated in the widget.
1357 } else {
1358 // Create a new thebes layer
1359 layer = mManager->CreateThebesLayer();
1360 if (!layer)
1361 return nullptr;
1362 // Mark this layer as being used for Thebes-painting display items
1363 data = new ThebesDisplayItemLayerUserData();
1364 layer->SetUserData(&gThebesDisplayItemLayerUserData, data);
1365 ResetScrollPositionForLayerPixelAlignment(aActiveScrolledRoot);
1366 #ifndef MOZ_ANDROID_OMTC
1367 didResetScrollPositionForLayerPixelAlignment = true;
1368 #endif
1370 data->mXScale = mParameters.mXScale;
1371 data->mYScale = mParameters.mYScale;
1372 data->mLastActiveScrolledRootOrigin = data->mActiveScrolledRootOrigin;
1373 data->mActiveScrolledRootOrigin = aTopLeft;
1374 data->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel;
1375 layer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation());
1377 mLayerBuilder->SaveLastPaintOffset(layer);
1379 // Set up transform so that 0,0 in the Thebes layer corresponds to the
1380 // (pixel-snapped) top-left of the aActiveScrolledRoot.
1381 nsPoint offset = aActiveScrolledRoot->GetOffsetToCrossDoc(aReferenceFrame);
1382 nscoord appUnitsPerDevPixel = aActiveScrolledRoot->PresContext()->AppUnitsPerDevPixel();
1383 gfxPoint scaledOffset(
1384 NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale,
1385 NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale);
1386 // We call RoundToMatchResidual here so that the residual after rounding
1387 // is close to data->mActiveScrolledRootPosition if possible.
1388 nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, data->mActiveScrolledRootPosition.x),
1389 RoundToMatchResidual(scaledOffset.y, data->mActiveScrolledRootPosition.y));
1390 data->mTranslation = pixOffset;
1391 pixOffset += mParameters.mOffset;
1392 gfxMatrix matrix;
1393 matrix.Translate(gfxPoint(pixOffset.x, pixOffset.y));
1394 layer->SetBaseTransform(gfx3DMatrix::From2D(matrix));
1396 // FIXME: Temporary workaround for bug 681192 and bug 724786.
1397 #ifndef MOZ_ANDROID_OMTC
1398 // Calculate exact position of the top-left of the active scrolled root.
1399 // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
1400 gfxPoint activeScrolledRootTopLeft = scaledOffset - matrix.GetTranslation() + mParameters.mOffset;
1401 // If it has changed, then we need to invalidate the entire layer since the
1402 // pixels in the layer buffer have the content at a (subpixel) offset
1403 // from what we need.
1404 if (!activeScrolledRootTopLeft.WithinEpsilonOf(data->mActiveScrolledRootPosition, SUBPIXEL_OFFSET_EPSILON)) {
1405 data->mActiveScrolledRootPosition = activeScrolledRootTopLeft;
1406 InvalidateEntireThebesLayer(layer, aActiveScrolledRoot);
1407 } else if (didResetScrollPositionForLayerPixelAlignment) {
1408 data->mActiveScrolledRootPosition = activeScrolledRootTopLeft;
1410 #endif
1412 return layer.forget();
1415 #ifdef MOZ_DUMP_PAINTING
1417 * Returns the appunits per dev pixel for the item's frame. The item must
1418 * have a frame because only nsDisplayClip items don't have a frame,
1419 * and those items are flattened away by ProcessDisplayItems.
1421 static int32_t
1422 AppUnitsPerDevPixel(nsDisplayItem* aItem)
1424 // The underlying frame for zoom items is the root frame of the subdocument.
1425 // But zoom display items report their bounds etc using the parent document's
1426 // APD because zoom items act as a conversion layer between the two different
1427 // APDs.
1428 if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) {
1429 return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
1431 return aItem->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
1433 #endif
1436 * Restrict the visible region of aLayer to the region that is actually visible.
1437 * Because we only reduce the visible region here, we don't need to worry
1438 * about whether CONTENT_OPAQUE is set; if layer was opauqe in the old
1439 * visible region, it will still be opaque in the new one.
1440 * @param aItemVisible the visible region of the display item (that is,
1441 * after any layer transform has been applied)
1443 static void
1444 RestrictVisibleRegionForLayer(Layer* aLayer, const nsIntRect& aItemVisible)
1446 gfx3DMatrix transform = aLayer->GetTransform();
1448 // if 'transform' is not invertible, then nothing will be displayed
1449 // for the layer, so it doesn't really matter what we do here
1450 gfxRect itemVisible(aItemVisible.x, aItemVisible.y, aItemVisible.width, aItemVisible.height);
1451 gfxRect layerVisible = transform.Inverse().ProjectRectBounds(itemVisible);
1452 layerVisible.RoundOut();
1454 nsIntRect visibleRect;
1455 if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect))
1456 return;
1458 nsIntRegion rgn = aLayer->GetVisibleRegion();
1459 if (!visibleRect.Contains(rgn.GetBounds())) {
1460 rgn.And(rgn, visibleRect);
1461 aLayer->SetVisibleRegion(rgn);
1465 nscolor
1466 ContainerState::FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex)
1468 ThebesLayerData* target = mThebesLayerDataStack[aThebesLayerIndex];
1469 for (int32_t i = aThebesLayerIndex - 1; i >= 0; --i) {
1470 ThebesLayerData* candidate = mThebesLayerDataStack[i];
1471 nsIntRegion visibleAboveIntersection;
1472 visibleAboveIntersection.And(candidate->mVisibleAboveRegion, target->mVisibleRegion);
1473 if (!visibleAboveIntersection.IsEmpty()) {
1474 // Some non-Thebes content between target and candidate; this is
1475 // hopeless
1476 break;
1479 nsIntRegion intersection;
1480 intersection.And(candidate->mVisibleRegion, target->mVisibleRegion);
1481 if (intersection.IsEmpty()) {
1482 // The layer doesn't intersect our target, ignore it and move on
1483 continue;
1486 // The candidate intersects our target. If any layer has a solid-color
1487 // area behind our target, this must be it. Scan its display items.
1488 nsIntRect deviceRect = target->mVisibleRegion.GetBounds();
1489 nsRect appUnitRect = deviceRect.ToAppUnits(mAppUnitsPerDevPixel);
1490 appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale);
1492 FrameLayerBuilder::ThebesLayerItemsEntry* entry =
1493 mLayerBuilder->GetThebesLayerItemsEntry(candidate->mLayer);
1494 NS_ASSERTION(entry, "Must know about this layer!");
1495 for (int32_t j = entry->mItems.Length() - 1; j >= 0; --j) {
1496 nsDisplayItem* item = entry->mItems[j].mItem;
1497 bool snap;
1498 nsRect bounds = item->GetBounds(mBuilder, &snap);
1499 if (snap && mSnappingEnabled) {
1500 nsIntRect snappedBounds = ScaleToNearestPixels(bounds);
1501 if (!snappedBounds.Intersects(deviceRect))
1502 continue;
1504 if (!snappedBounds.Contains(deviceRect))
1505 break;
1507 } else {
1508 // The layer's visible rect is already (close enough to) pixel
1509 // aligned, so no need to round out and in here.
1510 if (!bounds.Intersects(appUnitRect))
1511 continue;
1513 if (!bounds.Contains(appUnitRect))
1514 break;
1517 nscolor color;
1518 if (item->IsUniform(mBuilder, &color) && NS_GET_A(color) == 255)
1519 return color;
1521 break;
1523 break;
1525 return NS_RGBA(0,0,0,0);
1528 void
1529 ContainerState::ThebesLayerData::UpdateCommonClipCount(
1530 const FrameLayerBuilder::Clip& aCurrentClip)
1532 if (mCommonClipCount >= 0) {
1533 int32_t end = std::min<int32_t>(aCurrentClip.mRoundedClipRects.Length(),
1534 mCommonClipCount);
1535 int32_t clipCount = 0;
1536 for (; clipCount < end; ++clipCount) {
1537 if (mItemClip.mRoundedClipRects[clipCount] !=
1538 aCurrentClip.mRoundedClipRects[clipCount]) {
1539 break;
1542 mCommonClipCount = clipCount;
1543 NS_ASSERTION(mItemClip.mRoundedClipRects.Length() >= uint32_t(mCommonClipCount),
1544 "Inconsistent common clip count.");
1545 } else {
1546 // first item in the layer
1547 mCommonClipCount = aCurrentClip.mRoundedClipRects.Length();
1551 already_AddRefed<ImageContainer>
1552 ContainerState::ThebesLayerData::CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder)
1554 if (!mImage) {
1555 return nullptr;
1558 return mImage->GetContainer(mLayer->Manager(), aBuilder);
1561 void
1562 ContainerState::PopThebesLayerData()
1564 NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop");
1566 int32_t lastIndex = mThebesLayerDataStack.Length() - 1;
1567 ThebesLayerData* data = mThebesLayerDataStack[lastIndex];
1569 nsRefPtr<Layer> layer;
1570 nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder);
1572 if ((data->mIsSolidColorInVisibleRegion || imageContainer) &&
1573 data->mLayer->GetValidRegion().IsEmpty()) {
1574 NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer),
1575 "Can't be a solid color as well as an image!");
1576 if (imageContainer) {
1577 nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer(data->mLayer);
1578 imageLayer->SetContainer(imageContainer);
1579 data->mImage->ConfigureLayer(imageLayer, mParameters.mOffset);
1580 imageLayer->SetPostScale(mParameters.mXScale,
1581 mParameters.mYScale);
1582 if (data->mItemClip.mHaveClipRect) {
1583 nsIntRect clip = ScaleToNearestPixels(data->mItemClip.mClipRect);
1584 clip.MoveBy(mParameters.mOffset);
1585 imageLayer->IntersectClipRect(clip);
1587 layer = imageLayer;
1588 mLayerBuilder->StoreOptimizedLayerForFrame(data->mImage,
1589 imageLayer);
1590 } else {
1591 nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer(data->mLayer);
1592 colorLayer->SetIsFixedPosition(data->mLayer->GetIsFixedPosition());
1593 colorLayer->SetColor(data->mSolidColor);
1595 // Copy transform
1596 colorLayer->SetBaseTransform(data->mLayer->GetBaseTransform());
1597 colorLayer->SetPostScale(data->mLayer->GetPostXScale(), data->mLayer->GetPostYScale());
1599 // Clip colorLayer to its visible region, since ColorLayers are
1600 // allowed to paint outside the visible region. Here we rely on the
1601 // fact that uniform display items fill rectangles; obviously the
1602 // area to fill must contain the visible region, and because it's
1603 // a rectangle, it must therefore contain the visible region's GetBounds.
1604 // Note that the visible region is already clipped appropriately.
1605 nsIntRect visibleRect = data->mVisibleRegion.GetBounds();
1606 visibleRect.MoveBy(mParameters.mOffset);
1607 colorLayer->SetClipRect(&visibleRect);
1609 layer = colorLayer;
1612 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
1613 AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer);
1614 NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?");
1615 mNewChildLayers.InsertElementAt(index + 1, layer);
1617 // Hide the ThebesLayer. We leave it in the layer tree so that we
1618 // can find and recycle it later.
1619 data->mLayer->IntersectClipRect(nsIntRect());
1620 data->mLayer->SetVisibleRegion(nsIntRegion());
1621 } else {
1622 layer = data->mLayer;
1623 imageContainer = nullptr;
1626 gfxMatrix transform;
1627 if (!layer->GetTransform().Is2D(&transform)) {
1628 NS_ERROR("Only 2D transformations currently supported");
1631 // ImageLayers are already configured with a visible region
1632 if (!imageContainer) {
1633 NS_ASSERTION(!transform.HasNonIntegerTranslation(),
1634 "Matrix not just an integer translation?");
1635 // Convert from relative to the container to relative to the
1636 // ThebesLayer itself.
1637 nsIntRegion rgn = data->mVisibleRegion;
1638 rgn.MoveBy(-GetTranslationForThebesLayer(data->mLayer));
1639 layer->SetVisibleRegion(rgn);
1642 nsIntRegion transparentRegion;
1643 transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
1644 bool isOpaque = transparentRegion.IsEmpty();
1645 // For translucent ThebesLayers, try to find an opaque background
1646 // color that covers the entire area beneath it so we can pull that
1647 // color into this layer to make it opaque.
1648 if (layer == data->mLayer) {
1649 nscolor backgroundColor = NS_RGBA(0,0,0,0);
1650 if (!isOpaque) {
1651 backgroundColor = FindOpaqueBackgroundColorFor(lastIndex);
1652 if (NS_GET_A(backgroundColor) == 255) {
1653 isOpaque = true;
1657 // Store the background color
1658 ThebesDisplayItemLayerUserData* userData =
1659 GetThebesDisplayItemLayerUserData(data->mLayer);
1660 NS_ASSERTION(userData, "where did our user data go?");
1661 if (userData->mForcedBackgroundColor != backgroundColor) {
1662 // Invalidate the entire target ThebesLayer since we're changing
1663 // the background color
1664 data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion());
1666 userData->mForcedBackgroundColor = backgroundColor;
1668 // use a mask layer for rounded rect clipping
1669 int32_t commonClipCount = data->mCommonClipCount;
1670 NS_ASSERTION(commonClipCount >= 0, "Inconsistent clip count.");
1671 SetupMaskLayer(layer, data->mItemClip, commonClipCount);
1672 // copy commonClipCount to the entry
1673 FrameLayerBuilder::ThebesLayerItemsEntry* entry = mLayerBuilder->
1674 GetThebesLayerItemsEntry(static_cast<ThebesLayer*>(layer.get()));
1675 entry->mCommonClipCount = commonClipCount;
1676 } else {
1677 // mask layer for image and color layers
1678 SetupMaskLayer(layer, data->mItemClip);
1680 uint32_t flags;
1681 if (isOpaque && !data->mForceTransparentSurface) {
1682 flags = Layer::CONTENT_OPAQUE;
1683 } else if (data->mNeedComponentAlpha) {
1684 flags = Layer::CONTENT_COMPONENT_ALPHA;
1685 } else {
1686 flags = 0;
1688 layer->SetContentFlags(flags);
1690 if (lastIndex > 0) {
1691 // Since we're going to pop off the last ThebesLayerData, the
1692 // mVisibleAboveRegion of the second-to-last item will need to include
1693 // the regions of the last item.
1694 ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1];
1695 nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
1696 data->mVisibleAboveRegion);
1697 nextData->mVisibleAboveRegion.Or(nextData->mVisibleAboveRegion,
1698 data->mVisibleRegion);
1699 nextData->mVisibleAboveRegion.SimplifyOutward(4);
1700 nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
1701 data->mDrawAboveRegion);
1702 nextData->mDrawAboveRegion.Or(nextData->mDrawAboveRegion,
1703 data->mDrawRegion);
1704 nextData->mDrawAboveRegion.SimplifyOutward(4);
1707 mThebesLayerDataStack.RemoveElementAt(lastIndex);
1710 static bool
1711 SuppressComponentAlpha(nsDisplayListBuilder* aBuilder,
1712 nsDisplayItem* aItem,
1713 const nsRect& aComponentAlphaBounds)
1715 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
1716 if (!windowTransparentRegion || windowTransparentRegion->IsEmpty())
1717 return false;
1719 // Suppress component alpha for items in the toplevel window that are over
1720 // the window translucent area
1721 nsIFrame* f = aItem->GetUnderlyingFrame();
1722 nsIFrame* ref = aBuilder->RootReferenceFrame();
1723 if (f->PresContext() != ref->PresContext())
1724 return false;
1726 for (nsIFrame* t = f; t; t = t->GetParent()) {
1727 if (t->IsTransformed())
1728 return false;
1731 return windowTransparentRegion->Intersects(aComponentAlphaBounds);
1734 static bool
1735 WindowHasTransparency(nsDisplayListBuilder* aBuilder)
1737 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion();
1738 return windowTransparentRegion && !windowTransparentRegion->IsEmpty();
1741 void
1742 ContainerState::ThebesLayerData::Accumulate(ContainerState* aState,
1743 nsDisplayItem* aItem,
1744 const nsIntRect& aVisibleRect,
1745 const nsIntRect& aDrawRect,
1746 const FrameLayerBuilder::Clip& aClip)
1748 if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) {
1749 mForceTransparentSurface = true;
1751 if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
1752 // Disable component alpha.
1753 // Note that the transform (if any) on the ThebesLayer is always an integer translation so
1754 // we don't have to factor that in here.
1755 aItem->DisableComponentAlpha();
1758 /* Mark as available for conversion to image layer if this is a nsDisplayImage and
1759 * we are the first visible item in the ThebesLayerData object.
1761 if (mVisibleRegion.IsEmpty() &&
1762 aItem->SupportsOptimizingToImage()) {
1763 mImage = static_cast<nsDisplayImageContainer*>(aItem);
1764 } else {
1765 mImage = nullptr;
1767 mItemClip = aClip;
1769 if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aDrawRect) &&
1770 mVisibleRegion.Contains(aVisibleRect)) {
1771 // A very common case! Most pages have a ThebesLayer with the page
1772 // background (opaque) visible and most or all of the page content over the
1773 // top of that background.
1774 // The rest of this method won't do anything. mVisibleRegion, mOpaqueRegion
1775 // and mDrawRegion don't need updating. mVisibleRegion contains aVisibleRect
1776 // already, mOpaqueRegion contains aDrawRect and therefore whatever
1777 // the opaque region of the item is. mDrawRegion must contain mOpaqueRegion
1778 // and therefore aDrawRect.
1779 NS_ASSERTION(mDrawRegion.Contains(aDrawRect), "Draw region not covered");
1780 return;
1783 nscolor uniformColor;
1784 bool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor);
1786 // Some display items have to exist (so they can set forceTransparentSurface
1787 // below) but don't draw anything. They'll return true for isUniform but
1788 // a color with opacity 0.
1789 if (!isUniform || NS_GET_A(uniformColor) > 0) {
1790 // Make sure that the visible area is covered by uniform pixels. In
1791 // particular this excludes cases where the edges of the item are not
1792 // pixel-aligned (thus the item will not be truly uniform).
1793 if (isUniform) {
1794 bool snap;
1795 nsRect bounds = aItem->GetBounds(aState->mBuilder, &snap);
1796 if (!aState->ScaleToInsidePixels(bounds, snap).Contains(aVisibleRect)) {
1797 isUniform = false;
1800 if (isUniform && aClip.mRoundedClipRects.IsEmpty()) {
1801 if (mVisibleRegion.IsEmpty()) {
1802 // This color is all we have
1803 mSolidColor = uniformColor;
1804 mIsSolidColorInVisibleRegion = true;
1805 } else if (mIsSolidColorInVisibleRegion &&
1806 mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect))) {
1807 // we can just blend the colors together
1808 mSolidColor = NS_ComposeColors(mSolidColor, uniformColor);
1809 } else {
1810 mIsSolidColorInVisibleRegion = false;
1812 } else {
1813 mIsSolidColorInVisibleRegion = false;
1816 mVisibleRegion.Or(mVisibleRegion, aVisibleRect);
1817 mVisibleRegion.SimplifyOutward(4);
1818 mDrawRegion.Or(mDrawRegion, aDrawRect);
1819 mDrawRegion.SimplifyOutward(4);
1822 bool snap;
1823 nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &snap);
1824 if (!opaque.IsEmpty()) {
1825 nsRegion opaqueClipped;
1826 nsRegionRectIterator iter(opaque);
1827 for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
1828 opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersect(*r));
1831 nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap);
1833 nsIntRegionRectIterator iter2(opaquePixels);
1834 for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) {
1835 // We don't use SimplifyInward here since it's not defined exactly
1836 // what it will discard. For our purposes the most important case
1837 // is a large opaque background at the bottom of z-order (e.g.,
1838 // a canvas background), so we need to make sure that the first rect
1839 // we see doesn't get discarded.
1840 nsIntRegion tmp;
1841 tmp.Or(mOpaqueRegion, *r);
1842 // Opaque display items in chrome documents whose window is partially
1843 // transparent are always added to the opaque region. This helps ensure
1844 // that we get as much subpixel-AA as possible in the chrome.
1845 if (tmp.GetNumRects() <= 4 ||
1846 (WindowHasTransparency(aState->mBuilder) &&
1847 aItem->GetUnderlyingFrame()->PresContext()->IsChrome())) {
1848 mOpaqueRegion = tmp;
1853 if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) {
1854 nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder);
1855 if (!componentAlpha.IsEmpty()) {
1856 nsIntRect componentAlphaRect =
1857 aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect);
1858 if (!mOpaqueRegion.Contains(componentAlphaRect)) {
1859 if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) {
1860 aItem->DisableComponentAlpha();
1861 } else {
1862 mNeedComponentAlpha = true;
1869 ContainerState::ThebesLayerData*
1870 ContainerState::FindThebesLayerFor(nsDisplayItem* aItem,
1871 const nsIntRect& aVisibleRect,
1872 const nsIntRect& aDrawRect,
1873 const FrameLayerBuilder::Clip& aClip,
1874 const nsIFrame* aActiveScrolledRoot,
1875 const nsPoint& aTopLeft)
1877 int32_t i;
1878 int32_t lowestUsableLayerWithScrolledRoot = -1;
1879 int32_t topmostLayerWithScrolledRoot = -1;
1880 for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) {
1881 ThebesLayerData* data = mThebesLayerDataStack[i];
1882 if (data->mDrawAboveRegion.Intersects(aVisibleRect)) {
1883 ++i;
1884 break;
1886 if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
1887 lowestUsableLayerWithScrolledRoot = i;
1888 if (topmostLayerWithScrolledRoot < 0) {
1889 topmostLayerWithScrolledRoot = i;
1892 if (data->mDrawRegion.Intersects(aVisibleRect))
1893 break;
1895 if (topmostLayerWithScrolledRoot < 0) {
1896 --i;
1897 for (; i >= 0; --i) {
1898 ThebesLayerData* data = mThebesLayerDataStack[i];
1899 if (data->mActiveScrolledRoot == aActiveScrolledRoot) {
1900 topmostLayerWithScrolledRoot = i;
1901 break;
1906 if (topmostLayerWithScrolledRoot >= 0) {
1907 while (uint32_t(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) {
1908 PopThebesLayerData();
1912 nsRefPtr<ThebesLayer> layer;
1913 ThebesLayerData* thebesLayerData = nullptr;
1914 if (lowestUsableLayerWithScrolledRoot < 0) {
1915 layer = CreateOrRecycleThebesLayer(aActiveScrolledRoot, aItem->ReferenceFrame(), aTopLeft);
1917 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???");
1918 mNewChildLayers.AppendElement(layer);
1920 thebesLayerData = new ThebesLayerData();
1921 mThebesLayerDataStack.AppendElement(thebesLayerData);
1922 thebesLayerData->mLayer = layer;
1923 thebesLayerData->mActiveScrolledRoot = aActiveScrolledRoot;
1924 } else {
1925 thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot];
1926 layer = thebesLayerData->mLayer;
1929 // check to see if the new item has rounded rect clips in common with
1930 // other items in the layer
1931 thebesLayerData->UpdateCommonClipCount(aClip);
1933 thebesLayerData->Accumulate(this, aItem, aVisibleRect, aDrawRect, aClip);
1935 return thebesLayerData;
1938 #ifdef MOZ_DUMP_PAINTING
1939 static void
1940 DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf)
1942 nsCString string(aItem->Name());
1943 string.Append("-");
1944 string.AppendInt((uint64_t)aItem);
1945 fprintf(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading());
1946 aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile);
1947 fprintf(gfxUtils::sDumpPaintFile, "\";");
1949 #endif
1951 static void
1952 PaintInactiveLayer(nsDisplayListBuilder* aBuilder,
1953 LayerManager* aManager,
1954 nsDisplayItem* aItem,
1955 gfxContext* aContext,
1956 nsRenderingContext* aCtx)
1958 // This item has an inactive layer. Render it to a ThebesLayer
1959 // using a temporary BasicLayerManager.
1960 BasicLayerManager* basic = static_cast<BasicLayerManager*>(aManager);
1961 nsRefPtr<gfxContext> context = aContext;
1962 #ifdef MOZ_DUMP_PAINTING
1963 int32_t appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem);
1964 nsIntRect itemVisibleRect =
1965 aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel);
1967 nsRefPtr<gfxASurface> surf;
1968 if (gfxUtils::sDumpPainting) {
1969 surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size(),
1970 gfxASurface::CONTENT_COLOR_ALPHA);
1971 surf->SetDeviceOffset(-itemVisibleRect.TopLeft());
1972 context = new gfxContext(surf);
1974 #endif
1975 basic->SetTarget(context);
1977 if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) {
1978 static_cast<nsDisplaySVGEffects*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic);
1979 if (basic->InTransaction()) {
1980 basic->AbortTransaction();
1982 } else {
1983 basic->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
1985 FrameLayerBuilder *builder = static_cast<FrameLayerBuilder*>(basic->GetUserData(&gLayerManagerLayerBuilder));
1986 if (builder) {
1987 builder->DidEndTransaction();
1990 basic->SetTarget(nullptr);
1992 #ifdef MOZ_DUMP_PAINTING
1993 if (gfxUtils::sDumpPainting) {
1994 DumpPaintedImage(aItem, surf);
1996 surf->SetDeviceOffset(gfxPoint(0, 0));
1997 aContext->SetSource(surf, itemVisibleRect.TopLeft());
1998 aContext->Rectangle(itemVisibleRect);
1999 aContext->Fill();
2000 aItem->SetPainted();
2002 #endif
2006 * Chooses a single active scrolled root for the entire display list, used
2007 * when we are flattening layers.
2009 bool
2010 ContainerState::ChooseActiveScrolledRoot(const nsDisplayList& aList,
2011 const nsIFrame **aActiveScrolledRoot)
2013 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
2014 nsDisplayItem::Type type = item->GetType();
2015 if (type == nsDisplayItem::TYPE_CLIP ||
2016 type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
2017 if (ChooseActiveScrolledRoot(*item->GetSameCoordinateSystemChildren(),
2018 aActiveScrolledRoot)) {
2019 return true;
2021 continue;
2024 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
2025 // Don't use an item that won't be part of any ThebesLayers to pick the
2026 // active scrolled root.
2027 if (layerState == LAYER_ACTIVE_FORCE) {
2028 continue;
2031 // Try using the actual active scrolled root of the backmost item, as that
2032 // should result in the least invalidation when scrolling.
2033 mBuilder->IsFixedItem(item, aActiveScrolledRoot);
2034 if (*aActiveScrolledRoot) {
2035 return true;
2038 return false;
2042 * Iterate through the non-clip items in aList and its descendants.
2043 * For each item we compute the effective clip rect. Each item is assigned
2044 * to a layer. We invalidate the areas in ThebesLayers where an item
2045 * has moved from one ThebesLayer to another. Also,
2046 * aState->mInvalidThebesContent is invalidated in every ThebesLayer.
2047 * We set the clip rect for items that generated their own layer, and
2048 * create a mask layer to do any rounded rect clipping.
2049 * (ThebesLayers don't need a clip rect on the layer, we clip the items
2050 * individually when we draw them.)
2051 * We set the visible rect for all layers, although the actual setting
2052 * of visible rects for some ThebesLayers is deferred until the calling
2053 * of ContainerState::Finish.
2055 void
2056 ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
2057 FrameLayerBuilder::Clip& aClip,
2058 uint32_t aFlags,
2059 const nsIFrame* aForceActiveScrolledRoot)
2061 SAMPLE_LABEL("ContainerState", "ProcessDisplayItems");
2063 const nsIFrame* lastActiveScrolledRoot = nullptr;
2064 nsPoint topLeft;
2066 // When NO_COMPONENT_ALPHA is set, items will be flattened into a single
2067 // layer, so we need to choose which active scrolled root to use for all
2068 // items.
2069 if (aFlags & NO_COMPONENT_ALPHA) {
2070 if (aForceActiveScrolledRoot) {
2071 lastActiveScrolledRoot = aForceActiveScrolledRoot;
2072 } else if (!ChooseActiveScrolledRoot(aList, &lastActiveScrolledRoot)) {
2073 lastActiveScrolledRoot = mContainerReferenceFrame;
2076 topLeft = lastActiveScrolledRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
2079 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) {
2080 nsDisplayItem::Type type = item->GetType();
2081 if (type == nsDisplayItem::TYPE_CLIP ||
2082 type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
2083 FrameLayerBuilder::Clip childClip(aClip, item);
2084 ProcessDisplayItems(*item->GetSameCoordinateSystemChildren(), childClip, aFlags, lastActiveScrolledRoot);
2085 continue;
2088 NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
2089 "items in a container layer should all have the same app units per dev pixel");
2091 nsIntRect itemVisibleRect =
2092 ScaleToOutsidePixels(item->GetVisibleRect(), false);
2093 bool snap;
2094 nsRect itemContent = item->GetBounds(mBuilder, &snap);
2095 nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
2096 if (aClip.mHaveClipRect) {
2097 itemContent.IntersectRect(itemContent, aClip.mClipRect);
2098 nsIntRect clipRect = ScaleToNearestPixels(aClip.mClipRect);
2099 itemDrawRect.IntersectRect(itemDrawRect, clipRect);
2101 mBounds.UnionRect(mBounds, itemContent);
2102 itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect);
2104 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters);
2105 if (layerState == LAYER_INACTIVE &&
2106 nsDisplayItem::ForceActiveLayers()) {
2107 layerState = LAYER_ACTIVE;
2110 bool isFixed;
2111 bool forceInactive;
2112 const nsIFrame* activeScrolledRoot;
2113 if (aFlags & NO_COMPONENT_ALPHA) {
2114 forceInactive = true;
2115 activeScrolledRoot = lastActiveScrolledRoot;
2116 isFixed = mBuilder->IsFixedItem(item, nullptr, activeScrolledRoot);
2117 } else {
2118 forceInactive = false;
2119 isFixed = mBuilder->IsFixedItem(item, &activeScrolledRoot);
2120 if (activeScrolledRoot != lastActiveScrolledRoot) {
2121 lastActiveScrolledRoot = activeScrolledRoot;
2122 topLeft = activeScrolledRoot->GetOffsetToCrossDoc(mContainerReferenceFrame);
2126 // Assign the item to a layer
2127 if (layerState == LAYER_ACTIVE_FORCE ||
2128 (layerState == LAYER_INACTIVE && !mManager->IsWidgetLayerManager()) ||
2129 (!forceInactive &&
2130 (layerState == LAYER_ACTIVE_EMPTY ||
2131 layerState == LAYER_ACTIVE))) {
2133 // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata.
2134 // We should never see an empty layer with any visible content!
2135 NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY ||
2136 itemVisibleRect.IsEmpty(),
2137 "State is LAYER_ACTIVE_EMPTY but visible rect is not.");
2139 // As long as the new layer isn't going to be a ThebesLayer,
2140 // InvalidateForLayerChange doesn't need the new layer pointer.
2141 // We also need to check the old data now, because BuildLayer
2142 // can overwrite it.
2143 InvalidateForLayerChange(item, nullptr, aClip, topLeft, nullptr);
2145 // If the item would have its own layer but is invisible, just hide it.
2146 // Note that items without their own layers can't be skipped this
2147 // way, since their ThebesLayer may decide it wants to draw them
2148 // into its buffer even if they're currently covered.
2149 if (itemVisibleRect.IsEmpty() && layerState != LAYER_ACTIVE_EMPTY) {
2150 continue;
2153 // Just use its layer.
2154 nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters);
2155 if (!ownLayer) {
2156 continue;
2159 NS_ASSERTION(!ownLayer->AsThebesLayer(),
2160 "Should never have created a dedicated Thebes layer!");
2162 nsRect invalid;
2163 if (item->IsInvalid(invalid)) {
2164 ownLayer->SetInvalidRectToVisibleRegion();
2167 // If it's not a ContainerLayer, we need to apply the scale transform
2168 // ourselves.
2169 if (!ownLayer->AsContainerLayer()) {
2170 ownLayer->SetPostScale(mParameters.mXScale,
2171 mParameters.mYScale);
2174 // If a transform layer is marked as fixed then the shadow transform gets
2175 // overwritten by CompositorParent when doing scroll compensation on
2176 // fixed layers. This means we need to make sure transform layers are not
2177 // marked as fixed.
2178 ownLayer->SetIsFixedPosition(isFixed && type != nsDisplayItem::TYPE_TRANSFORM);
2180 // Update that layer's clip and visible rects.
2181 NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager");
2182 NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData),
2183 "We shouldn't have a FrameLayerBuilder-managed layer here!");
2184 NS_ASSERTION(aClip.mHaveClipRect ||
2185 aClip.mRoundedClipRects.IsEmpty(),
2186 "If we have rounded rects, we must have a clip rect");
2187 // It has its own layer. Update that layer's clip and visible rects.
2188 if (aClip.mHaveClipRect) {
2189 nsIntRect clip = ScaleToNearestPixels(aClip.NonRoundedIntersection());
2190 clip.MoveBy(mParameters.mOffset);
2191 ownLayer->IntersectClipRect(clip);
2193 ThebesLayerData* data = GetTopThebesLayerData();
2194 if (data) {
2195 data->mVisibleAboveRegion.Or(data->mVisibleAboveRegion, itemVisibleRect);
2196 data->mVisibleAboveRegion.SimplifyOutward(4);
2197 // Add the entire bounds rect to the mDrawAboveRegion.
2198 // The visible region may be excluding opaque content above the
2199 // item, and we need to ensure that that content is not placed
2200 // in a ThebesLayer below the item!
2201 data->mDrawAboveRegion.Or(data->mDrawAboveRegion, itemDrawRect);
2202 data->mDrawAboveRegion.SimplifyOutward(4);
2204 itemVisibleRect.MoveBy(mParameters.mOffset);
2205 RestrictVisibleRegionForLayer(ownLayer, itemVisibleRect);
2207 // rounded rectangle clipping using mask layers
2208 // (must be done after visible rect is set on layer)
2209 if (aClip.IsRectClippedByRoundedCorner(itemContent)) {
2210 SetupMaskLayer(ownLayer, aClip);
2213 ContainerLayer* oldContainer = ownLayer->GetParent();
2214 if (oldContainer && oldContainer != mContainerLayer) {
2215 oldContainer->RemoveChild(ownLayer);
2217 NS_ASSERTION(!mNewChildLayers.Contains(ownLayer),
2218 "Layer already in list???");
2220 mNewChildLayers.AppendElement(ownLayer);
2223 * No need to allocate geometry for items that aren't
2224 * part of a ThebesLayer.
2226 nsAutoPtr<nsDisplayItemGeometry> dummy;
2227 mLayerBuilder->AddLayerDisplayItem(ownLayer, item,
2228 aClip, layerState,
2229 topLeft, nullptr,
2230 dummy);
2231 } else {
2232 ThebesLayerData* data =
2233 FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, aClip,
2234 activeScrolledRoot, topLeft);
2236 data->mLayer->SetIsFixedPosition(isFixed);
2238 nsAutoPtr<nsDisplayItemGeometry> geometry(item->AllocateGeometry(mBuilder));
2240 InvalidateForLayerChange(item, data->mLayer, aClip, topLeft, geometry);
2242 mLayerBuilder->AddThebesDisplayItem(data->mLayer, item, aClip,
2243 mContainerFrame,
2244 layerState, topLeft,
2245 geometry);
2247 // check to see if the new item has rounded rect clips in common with
2248 // other items in the layer
2249 data->UpdateCommonClipCount(aClip);
2255 * Combine two clips and returns true if clipping
2256 * needs to be applied.
2258 * @param aClip Current clip
2259 * @param aOldClip Optional clip from previous paint.
2260 * @param aShift Offet to apply to aOldClip
2261 * @param aCombined Outparam - Computed clip region
2262 * @return True if the clip should be applied, false
2263 * otherwise.
2265 static bool ComputeCombinedClip(const FrameLayerBuilder::Clip& aClip,
2266 FrameLayerBuilder::Clip* aOldClip,
2267 const nsPoint& aShift,
2268 nsRegion& aCombined)
2270 if (!aClip.mHaveClipRect ||
2271 (aOldClip && !aOldClip->mHaveClipRect)) {
2272 return false;
2275 if (aOldClip) {
2276 aCombined = aOldClip->NonRoundedIntersection();
2277 aCombined.MoveBy(aShift);
2278 aCombined.Or(aCombined, aClip.NonRoundedIntersection());
2279 } else {
2280 aCombined = aClip.NonRoundedIntersection();
2282 return true;
2285 void
2286 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem,
2287 Layer* aNewLayer,
2288 const FrameLayerBuilder::Clip& aClip,
2289 const nsPoint& aTopLeft,
2290 nsDisplayItemGeometry *aGeometry)
2292 NS_ASSERTION(aItem->GetUnderlyingFrame(),
2293 "Display items that render using Thebes must have a frame");
2294 NS_ASSERTION(aItem->GetPerFrameKey(),
2295 "Display items that render using Thebes must have a key");
2296 nsDisplayItemGeometry *oldGeometry = NULL;
2297 FrameLayerBuilder::Clip* oldClip = NULL;
2298 nsAutoTArray<nsIFrame*,4> changedFrames;
2299 bool isInvalid = false;
2300 Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid);
2301 if (aNewLayer != oldLayer && oldLayer) {
2302 // The item has changed layers.
2303 // Invalidate the old bounds in the old layer and new bounds in the new layer.
2304 ThebesLayer* t = oldLayer->AsThebesLayer();
2305 if (t) {
2306 // Note that whenever the layer's scale changes, we invalidate the whole thing,
2307 // so it doesn't matter whether we are using the old scale at last paint
2308 // or a new scale here
2309 #ifdef DEBUG_INVALIDATIONS
2310 printf("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->GetUnderlyingFrame(), t, aNewLayer);
2311 #endif
2312 InvalidatePostTransformRegion(t,
2313 oldGeometry->ComputeInvalidationRegion(),
2314 *oldClip,
2315 mLayerBuilder->GetLastPaintOffset(t));
2317 if (aNewLayer) {
2318 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer();
2319 if (newThebesLayer) {
2320 InvalidatePostTransformRegion(newThebesLayer,
2321 aGeometry->ComputeInvalidationRegion(),
2322 aClip,
2323 GetTranslationForThebesLayer(newThebesLayer));
2326 aItem->NotifyRenderingChanged();
2327 return;
2329 if (!aNewLayer) {
2330 return;
2333 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer();
2334 if (!newThebesLayer) {
2335 return;
2338 ThebesDisplayItemLayerUserData* data =
2339 static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData));
2340 // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to
2341 // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas.
2342 // If we do get an invalid rect, then we want to add this on top of the change areas.
2343 nsRect invalid;
2344 nsRegion combined;
2345 nsPoint shift = aTopLeft - data->mLastActiveScrolledRootOrigin;
2346 if (!oldLayer) {
2347 // This item is being added for the first time, invalidate its entire area.
2348 //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this.
2349 combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion());
2350 #ifdef DEBUG_INVALIDATIONS
2351 printf("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->GetUnderlyingFrame(), aNewLayer);
2352 #endif
2353 } else if (isInvalid || (aItem->IsInvalid(invalid) && invalid.IsEmpty())) {
2354 // Either layout marked item as needing repainting, invalidate the entire old and new areas.
2355 combined = oldClip->ApplyNonRoundedIntersection(oldGeometry->ComputeInvalidationRegion());
2356 combined.MoveBy(shift);
2357 combined.Or(combined, aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion()));
2358 #ifdef DEBUG_INVALIDATIONS
2359 printf("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->GetUnderlyingFrame(), aNewLayer);
2360 #endif
2361 } else {
2362 // Let the display item check for geometry changes and decide what needs to be
2363 // repainted.
2364 oldGeometry->MoveBy(shift);
2365 aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined);
2366 oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(),
2367 aClip, aGeometry->ComputeInvalidationRegion(),
2368 &combined);
2370 // Add in any rect that the frame specified
2371 combined.Or(combined, invalid);
2373 for (uint32_t i = 0; i < changedFrames.Length(); i++) {
2374 combined.Or(combined, changedFrames[i]->GetVisualOverflowRect());
2377 // Restrict invalidation to the clipped region
2378 nsRegion clip;
2379 if (ComputeCombinedClip(aClip, oldClip, shift, clip)) {
2380 combined.And(combined, clip);
2382 #ifdef DEBUG_INVALIDATIONS
2383 if (!combined.IsEmpty()) {
2384 printf("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->GetUnderlyingFrame(), aNewLayer);
2386 #endif
2388 if (!combined.IsEmpty()) {
2389 aItem->NotifyRenderingChanged();
2390 InvalidatePostTransformRegion(newThebesLayer,
2391 combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel),
2392 GetTranslationForThebesLayer(newThebesLayer));
2396 void
2397 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer* aLayer,
2398 nsDisplayItem* aItem,
2399 const Clip& aClip,
2400 nsIFrame* aContainerLayerFrame,
2401 LayerState aLayerState,
2402 const nsPoint& aTopLeft,
2403 nsAutoPtr<nsDisplayItemGeometry> aGeometry)
2405 ThebesDisplayItemLayerUserData* thebesData =
2406 static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
2407 nsRefPtr<LayerManager> tempManager;
2408 nsIntRect intClip;
2409 bool hasClip = false;
2410 if (aLayerState != LAYER_NONE) {
2411 DisplayItemData *data = GetDisplayItemDataForManager(aItem, aLayer->Manager());
2412 if (data) {
2413 tempManager = data->mInactiveManager;
2415 if (!tempManager) {
2416 tempManager = new BasicLayerManager();
2419 // We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
2420 nsRegion clip;
2421 FrameLayerBuilder::Clip* oldClip = nullptr;
2422 GetOldLayerFor(aItem, nullptr, &oldClip);
2423 hasClip = ComputeCombinedClip(aClip, oldClip,
2424 aTopLeft - thebesData->mLastActiveScrolledRootOrigin,
2425 clip);
2427 if (hasClip) {
2428 intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale,
2429 thebesData->mYScale,
2430 thebesData->mAppUnitsPerDevPixel);
2434 AddLayerDisplayItem(aLayer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry);
2436 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
2437 if (entry) {
2438 entry->mContainerLayerFrame = aContainerLayerFrame;
2439 if (entry->mContainerLayerGeneration == 0) {
2440 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2442 NS_ASSERTION(aItem->GetUnderlyingFrame(), "Must have frame");
2443 if (tempManager) {
2444 FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
2445 layerBuilder->Init(mDisplayListBuilder, tempManager);
2447 tempManager->BeginTransaction();
2448 if (mRetainingManager) {
2449 layerBuilder->DidBeginRetainedLayerTransaction(tempManager);
2452 nsAutoPtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot()));
2453 nsRefPtr<Layer> layer =
2454 aItem->BuildLayer(mDisplayListBuilder, tempManager, FrameLayerBuilder::ContainerParameters());
2455 // We have no easy way of detecting if this transaction will ever actually get finished.
2456 // For now, I've just silenced the warning with nested transactions in BasicLayers.cpp
2457 if (!layer) {
2458 tempManager->EndTransaction(nullptr, nullptr);
2459 tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr);
2460 return;
2463 // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been
2464 // stored in layerBuilder. Manually add it now.
2465 if (mRetainingManager) {
2466 #ifdef DEBUG_DISPLAY_ITEM_DATA
2467 LayerManagerData* parentLmd = static_cast<LayerManagerData*>
2468 (aLayer->Manager()->GetUserData(&gLayerManagerUserData));
2469 LayerManagerData* lmd = static_cast<LayerManagerData*>
2470 (tempManager->GetUserData(&gLayerManagerUserData));
2471 lmd->mParent = parentLmd;
2472 #endif
2473 layerBuilder->StoreDataForFrame(aItem, layer, LAYER_ACTIVE);
2476 tempManager->SetRoot(layer);
2477 layerBuilder->WillEndTransaction();
2479 nsIntPoint offset = GetLastPaintOffset(aLayer) - GetTranslationForThebesLayer(aLayer);
2480 props->MoveBy(-offset);
2481 nsIntRegion invalid = props->ComputeDifferences(layer, nullptr);
2482 if (aLayerState == LAYER_SVG_EFFECTS) {
2483 invalid = nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(aItem->GetUnderlyingFrame(),
2484 aItem->ToReferenceFrame(),
2485 invalid.GetBounds());
2487 if (!invalid.IsEmpty()) {
2488 #ifdef DEBUG_INVALIDATIONS
2489 printf("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->GetUnderlyingFrame(), aLayer);
2490 #endif
2491 if (hasClip) {
2492 invalid.And(invalid, intClip);
2495 invalid.ScaleRoundOut(thebesData->mXScale, thebesData->mYScale);
2496 InvalidatePostTransformRegion(aLayer, invalid,
2497 GetTranslationForThebesLayer(aLayer));
2500 ClippedDisplayItem* cdi =
2501 entry->mItems.AppendElement(ClippedDisplayItem(aItem, aClip,
2502 mContainerLayerGeneration));
2503 cdi->mInactiveLayerManager = tempManager;
2507 FrameLayerBuilder::DisplayItemData*
2508 FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState)
2510 DisplayItemData* oldData = GetDisplayItemDataForManager(aItem, mRetainingManager);
2511 if (oldData) {
2512 if (!oldData->mUsed) {
2513 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration, aItem);
2515 return oldData;
2518 LayerManagerData* lmd = static_cast<LayerManagerData*>
2519 (mRetainingManager->GetUserData(&gLayerManagerUserData));
2521 nsRefPtr<DisplayItemData> data =
2522 new DisplayItemData(lmd, aItem->GetPerFrameKey(),
2523 aLayer, aState, mContainerLayerGeneration);
2525 data->AddFrame(aItem->GetUnderlyingFrame());
2527 nsAutoTArray<nsIFrame*,4> mergedFrames;
2528 aItem->GetMergedFrames(&mergedFrames);
2530 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) {
2531 data->AddFrame(mergedFrames[i]);
2534 lmd->mDisplayItems.PutEntry(data);
2535 return data;
2538 void
2539 FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame,
2540 uint32_t aDisplayItemKey,
2541 Layer* aLayer,
2542 LayerState aState)
2544 DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey);
2545 if (oldData && oldData->mFrameList.Length() == 1) {
2546 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration);
2547 return;
2550 LayerManagerData* lmd = static_cast<LayerManagerData*>
2551 (mRetainingManager->GetUserData(&gLayerManagerUserData));
2553 nsRefPtr<DisplayItemData> data =
2554 new DisplayItemData(lmd, aDisplayItemKey, aLayer,
2555 aState, mContainerLayerGeneration);
2557 data->AddFrame(aFrame);
2559 lmd->mDisplayItems.PutEntry(data);
2562 FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem()
2564 if (mInactiveLayerManager) {
2565 // We always start a transaction during layer construction for all inactive
2566 // layers, but we don't necessarily call EndTransaction during painting.
2567 // If the transaaction is still open, end it to avoid assertions.
2568 BasicLayerManager* basic = static_cast<BasicLayerManager*>(mInactiveLayerManager.get());
2569 if (basic->InTransaction()) {
2570 basic->EndTransaction(nullptr, nullptr);
2572 basic->SetUserData(&gLayerManagerLayerBuilder, nullptr);
2576 void
2577 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer,
2578 nsDisplayItem* aItem,
2579 const Clip& aClip,
2580 LayerState aLayerState,
2581 const nsPoint& aTopLeft,
2582 LayerManager* aManager,
2583 nsAutoPtr<nsDisplayItemGeometry> aGeometry)
2585 if (aLayer->Manager() != mRetainingManager)
2586 return;
2588 DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState);
2589 ThebesLayer *t = aLayer->AsThebesLayer();
2590 if (t) {
2591 data->mGeometry = aGeometry;
2592 data->mClip = aClip;
2594 data->mInactiveManager = aManager;
2597 nsIntPoint
2598 FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer)
2600 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
2601 if (entry) {
2602 if (entry->mContainerLayerGeneration == 0) {
2603 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2605 if (entry->mHasExplicitLastPaintOffset)
2606 return entry->mLastPaintOffset;
2608 return GetTranslationForThebesLayer(aLayer);
2611 void
2612 FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer)
2614 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer);
2615 if (entry) {
2616 if (entry->mContainerLayerGeneration == 0) {
2617 entry->mContainerLayerGeneration = mContainerLayerGeneration;
2619 entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer);
2620 entry->mHasExplicitLastPaintOffset = true;
2624 void
2625 ContainerState::CollectOldLayers()
2627 for (Layer* layer = mContainerLayer->GetFirstChild(); layer;
2628 layer = layer->GetNextSibling()) {
2629 NS_ASSERTION(!layer->HasUserData(&gMaskLayerUserData),
2630 "Mask layer in layer tree; could not be recycled.");
2631 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
2632 NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type");
2633 mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer));
2636 if (Layer* maskLayer = layer->GetMaskLayer()) {
2637 NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE,
2638 "Could not recycle mask layer, unsupported layer type.");
2639 mRecycledMaskImageLayers.Put(layer, static_cast<ImageLayer*>(maskLayer));
2644 void
2645 ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData)
2647 while (!mThebesLayerDataStack.IsEmpty()) {
2648 PopThebesLayerData();
2651 uint32_t textContentFlags = 0;
2653 // Make sure that current/existing layers are added to the parent and are
2654 // in the correct order.
2655 Layer* layer = nullptr;
2656 for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) {
2657 Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get();
2658 layer = mNewChildLayers[i];
2660 if (!layer->GetVisibleRegion().IsEmpty()) {
2661 textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA;
2664 if (!layer->GetParent()) {
2665 // This is not currently a child of the container, so just add it
2666 // now.
2667 mContainerLayer->InsertAfter(layer, prevChild);
2668 continue;
2671 NS_ASSERTION(layer->GetParent() == mContainerLayer,
2672 "Layer shouldn't be the child of some other container");
2673 mContainerLayer->RepositionChild(layer, prevChild);
2676 // Remove old layers that have become unused.
2677 if (!layer) {
2678 layer = mContainerLayer->GetFirstChild();
2679 } else {
2680 layer = layer->GetNextSibling();
2682 while (layer) {
2683 Layer *layerToRemove = layer;
2684 layer = layer->GetNextSibling();
2685 mContainerLayer->RemoveChild(layerToRemove);
2688 *aTextContentFlags = textContentFlags;
2691 static FrameLayerBuilder::ContainerParameters
2692 ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder,
2693 nsDisplayListBuilder* aDisplayListBuilder,
2694 nsIFrame* aContainerFrame,
2695 const gfx3DMatrix* aTransform,
2696 const FrameLayerBuilder::ContainerParameters& aIncomingScale,
2697 ContainerLayer* aLayer,
2698 LayerState aState)
2700 nsIntPoint offset;
2702 gfx3DMatrix transform =
2703 gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0);
2704 if (aTransform) {
2705 // aTransform is applied first, then the scale is applied to the result
2706 transform = (*aTransform)*transform;
2707 // Set any matrix entries close to integers to be those exact integers.
2708 // This protects against floating-point inaccuracies causing problems
2709 // in the checks below.
2710 transform.NudgeToIntegers();
2712 gfxMatrix transform2d;
2713 if (aContainerFrame &&
2714 aState == LAYER_INACTIVE &&
2715 (!aTransform || (aTransform->Is2D(&transform2d) &&
2716 !transform2d.HasNonTranslation()))) {
2717 // When we have an inactive ContainerLayer, translate the container by the offset to the
2718 // reference frame (and offset all child layers by the reverse) so that the coordinate
2719 // space of the child layers isn't affected by scrolling.
2720 // This gets confusing for complicated transform (since we'd have to compute the scale
2721 // factors for the matrix), so we don't bother. Any frames that are building an nsDisplayTransform
2722 // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't
2723 // matter.
2724 nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame);
2725 nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
2726 offset = nsIntPoint(
2727 int32_t(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale),
2728 int32_t(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale));
2730 transform = transform * gfx3DMatrix::Translation(offset.x + aIncomingScale.mOffset.x, offset.y + aIncomingScale.mOffset.y, 0);
2733 bool canDraw2D = transform.CanDraw2D(&transform2d);
2734 gfxSize scale;
2735 bool isRetained = aLayer->Manager()->IsWidgetLayerManager();
2736 // Only fiddle with scale factors for the retaining layer manager, since
2737 // it only matters for retained layers
2738 // XXX Should we do something for 3D transforms?
2739 if (canDraw2D && isRetained) {
2740 // If the container's transform is animated off main thread, then use the
2741 // maximum scale.
2742 if (aContainerFrame->GetContent() &&
2743 nsLayoutUtils::HasAnimationsForCompositor(
2744 aContainerFrame->GetContent(), eCSSProperty_transform)) {
2745 scale = nsLayoutUtils::GetMaximumAnimatedScale(aContainerFrame->GetContent());
2746 } else {
2747 //Scale factors are normalized to a power of 2 to reduce the number of resolution changes
2748 scale = transform2d.ScaleFactors(true);
2749 // For frames with a changing transform that's not just a translation,
2750 // round scale factors up to nearest power-of-2 boundary so that we don't
2751 // keep having to redraw the content as it scales up and down. Rounding up to nearest
2752 // power-of-2 boundary ensures we never scale up, only down --- avoiding
2753 // jaggies. It also ensures we never scale down by more than a factor of 2,
2754 // avoiding bad downscaling quality.
2755 gfxMatrix frameTransform;
2756 if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer) &&
2757 aTransform &&
2758 (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) {
2759 // Don't clamp the scale factor when the new desired scale factor matches the old one
2760 // or it was previously unscaled.
2761 bool clamp = true;
2762 gfxMatrix oldFrameTransform2d;
2763 if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) {
2764 gfxSize oldScale = oldFrameTransform2d.ScaleFactors(true);
2765 if (oldScale == scale || oldScale == gfxSize(1.0, 1.0)) {
2766 clamp = false;
2769 if (clamp) {
2770 scale.width = gfxUtils::ClampToScaleFactor(scale.width);
2771 scale.height = gfxUtils::ClampToScaleFactor(scale.height);
2773 } else {
2774 // XXX Do we need to move nearly-integer values to integers here?
2777 // If the scale factors are too small, just use 1.0. The content is being
2778 // scaled out of sight anyway.
2779 if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) {
2780 scale = gfxSize(1.0, 1.0);
2782 } else {
2783 scale = gfxSize(1.0, 1.0);
2786 // Store the inverse of our resolution-scale on the layer
2787 aLayer->SetBaseTransform(transform);
2788 aLayer->SetPreScale(1.0f/float(scale.width),
2789 1.0f/float(scale.height));
2790 aLayer->SetInheritedScale(aIncomingScale.mXScale,
2791 aIncomingScale.mYScale);
2793 FrameLayerBuilder::ContainerParameters
2794 result(scale.width, scale.height, -offset, aIncomingScale);
2795 if (aTransform) {
2796 result.mInTransformedSubtree = true;
2797 if (aContainerFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer)) {
2798 result.mInActiveTransformedSubtree = true;
2801 if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) {
2802 result.mDisableSubpixelAntialiasingInDescendants = true;
2804 return result;
2807 /* static */ PLDHashOperator
2808 FrameLayerBuilder::RestoreDisplayItemData(nsRefPtrHashKey<DisplayItemData>* aEntry, void* aUserArg)
2810 DisplayItemData* data = aEntry->GetKey();
2811 uint32_t *generation = static_cast<uint32_t*>(aUserArg);
2813 if (data->mUsed && data->mContainerLayerGeneration >= *generation) {
2814 return PL_DHASH_REMOVE;
2817 return PL_DHASH_NEXT;
2820 /* static */ PLDHashOperator
2821 FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg)
2823 uint32_t *generation = static_cast<uint32_t*>(aUserArg);
2825 if (aEntry->mContainerLayerGeneration >= *generation) {
2826 // We can just remove these items rather than attempting to revert them
2827 // because we're going to want to invalidate everything when transitioning
2828 // to component alpha flattening.
2829 return PL_DHASH_REMOVE;
2832 for (uint32_t i = 0; i < aEntry->mItems.Length(); i++) {
2833 if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) {
2834 aEntry->mItems.TruncateLength(i);
2835 return PL_DHASH_NEXT;
2839 return PL_DHASH_NEXT;
2842 already_AddRefed<ContainerLayer>
2843 FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
2844 LayerManager* aManager,
2845 nsIFrame* aContainerFrame,
2846 nsDisplayItem* aContainerItem,
2847 const nsDisplayList& aChildren,
2848 const ContainerParameters& aParameters,
2849 const gfx3DMatrix* aTransform)
2851 uint32_t containerDisplayItemKey =
2852 aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO;
2853 NS_ASSERTION(aContainerFrame, "Container display items here should have a frame");
2854 NS_ASSERTION(!aContainerItem ||
2855 aContainerItem->GetUnderlyingFrame() == aContainerFrame,
2856 "Container display item must match given frame");
2858 nsRefPtr<ContainerLayer> containerLayer;
2859 if (aManager == mRetainingManager) {
2860 // Using GetOldLayerFor will search merged frames, as well as the underlying
2861 // frame. The underlying frame can change when a page scrolls, so this
2862 // avoids layer recreation in the situation that a new underlying frame is
2863 // picked for a layer.
2864 Layer* oldLayer = nullptr;
2865 if (aContainerItem) {
2866 oldLayer = GetOldLayerFor(aContainerItem);
2867 } else {
2868 DisplayItemData *data = GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey);
2869 if (data) {
2870 oldLayer = data->mLayer;
2874 if (oldLayer) {
2875 NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager");
2876 if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) {
2877 // The old layer for this item is actually our ThebesLayer
2878 // because we rendered its layer into that ThebesLayer. So we
2879 // don't actually have a retained container layer.
2880 } else {
2881 NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER,
2882 "Wrong layer type");
2883 containerLayer = static_cast<ContainerLayer*>(oldLayer);
2884 // Clear clip rect; the caller will set it if necessary.
2885 containerLayer->SetClipRect(nullptr);
2886 containerLayer->SetMaskLayer(nullptr);
2890 if (!containerLayer) {
2891 // No suitable existing layer was found.
2892 containerLayer = aManager->CreateContainerLayer();
2893 if (!containerLayer)
2894 return nullptr;
2897 LayerState state = aContainerItem ? aContainerItem->GetLayerState(aBuilder, aManager, aParameters) : LAYER_ACTIVE;
2898 if (state == LAYER_INACTIVE &&
2899 nsDisplayItem::ForceActiveLayers()) {
2900 state = LAYER_ACTIVE;
2903 if (aContainerItem && state == LAYER_ACTIVE_EMPTY) {
2904 // Empty layers only have metadata and should never have display items. We
2905 // early exit because later, invalidation will walk up the frame tree to
2906 // determine which thebes layer gets invalidated. Since an empty layer
2907 // should never have anything to paint, it should never be invalidated.
2908 NS_ASSERTION(aChildren.IsEmpty(), "Should have no children");
2909 return containerLayer.forget();
2912 ContainerParameters scaleParameters =
2913 ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame, aTransform, aParameters,
2914 containerLayer, state);
2916 uint32_t oldGeneration = mContainerLayerGeneration;
2917 mContainerLayerGeneration = ++mMaxContainerLayerGeneration;
2919 nsRefPtr<RefCountedRegion> thebesLayerInvalidRegion = nullptr;
2920 if (mRetainingManager) {
2921 if (aContainerItem) {
2922 StoreDataForFrame(aContainerItem, containerLayer, LAYER_ACTIVE);
2923 } else {
2924 StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE);
2928 LayerManagerData* data = static_cast<LayerManagerData*>
2929 (aManager->GetUserData(&gLayerManagerUserData));
2931 nsRect bounds;
2932 nsIntRect pixBounds;
2933 int32_t appUnitsPerDevPixel;
2934 uint32_t stateFlags = 0;
2935 if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) &&
2936 mRetainingManager && !mRetainingManager->AreComponentAlphaLayersEnabled()) {
2937 stateFlags = ContainerState::NO_COMPONENT_ALPHA;
2939 uint32_t flags;
2940 while (true) {
2941 ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(),
2942 aContainerFrame, aContainerItem,
2943 containerLayer, scaleParameters);
2945 Clip clip;
2946 state.ProcessDisplayItems(aChildren, clip, stateFlags);
2948 // Set CONTENT_COMPONENT_ALPHA if any of our children have it.
2949 // This is suboptimal ... a child could have text that's over transparent
2950 // pixels in its own layer, but over opaque parts of previous siblings.
2951 state.Finish(&flags, data);
2952 bounds = state.GetChildrenBounds();
2953 pixBounds = state.ScaleToOutsidePixels(bounds, false);
2954 appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel();
2956 if ((flags & Layer::CONTENT_COMPONENT_ALPHA) &&
2957 mRetainingManager &&
2958 !mRetainingManager->AreComponentAlphaLayersEnabled() &&
2959 !stateFlags) {
2960 // Since we don't want any component alpha layers on BasicLayers, we repeat
2961 // the layer building process with this explicitely forced off.
2962 // We restore the previous FrameLayerBuilder state since the first set
2963 // of layer building will have changed it.
2964 stateFlags = ContainerState::NO_COMPONENT_ALPHA;
2965 data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData,
2966 &mContainerLayerGeneration);
2967 mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries,
2968 &mContainerLayerGeneration);
2969 aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA);
2970 continue;
2972 break;
2975 NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds");
2976 pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y));
2977 containerLayer->SetVisibleRegion(pixBounds);
2978 // Make sure that rounding the visible region out didn't add any area
2979 // we won't paint
2980 if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) {
2981 bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale);
2982 if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) {
2983 // Clear CONTENT_COMPONENT_ALPHA
2984 flags = Layer::CONTENT_OPAQUE;
2987 containerLayer->SetContentFlags(flags);
2989 mContainerLayerGeneration = oldGeneration;
2990 containerLayer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
2992 return containerLayer.forget();
2995 Layer*
2996 FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
2997 nsDisplayItem* aItem)
2999 NS_ASSERTION(aItem->GetUnderlyingFrame(),
3000 "Can only call GetLeafLayerFor on items that have a frame");
3001 Layer* layer = GetOldLayerFor(aItem);
3002 if (!layer)
3003 return nullptr;
3004 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3005 // This layer was created to render Thebes-rendered content for this
3006 // display item. The display item should not use it for its own
3007 // layer rendering.
3008 return nullptr;
3010 // Clear clip rect; the caller is responsible for setting it.
3011 layer->SetClipRect(nullptr);
3012 layer->SetMaskLayer(nullptr);
3013 return layer;
3016 /* static */ void
3017 FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager)
3019 LayerManagerData* data = static_cast<LayerManagerData*>
3020 (aManager->GetUserData(&gLayerManagerUserData));
3021 if (data) {
3022 data->mInvalidateAllLayers = true;
3026 /* static */ void
3027 FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame)
3029 nsTArray<DisplayItemData*> *array =
3030 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3031 if (array) {
3032 for (uint32_t i = 0; i < array->Length(); i++) {
3033 array->ElementAt(i)->mParent->mInvalidateAllLayers = true;
3038 /* static */
3039 Layer*
3040 FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey)
3042 //TODO: This isn't completely correct, since a frame could exist as a layer
3043 // in the normal widget manager, and as a different layer (or no layer)
3044 // in the secondary manager
3046 nsTArray<DisplayItemData*> *array =
3047 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3048 if (array) {
3049 for (uint32_t i = 0; i < array->Length(); i++) {
3050 DisplayItemData *element = array->ElementAt(i);
3051 if (!element->mParent->mLayerManager->IsWidgetLayerManager()) {
3052 continue;
3054 if (element->mDisplayItemKey == aDisplayItemKey) {
3055 if (element->mOptLayer) {
3056 return element->mOptLayer;
3059 Layer* layer = element->mLayer;
3060 if (!layer->HasUserData(&gColorLayerUserData) &&
3061 !layer->HasUserData(&gImageLayerUserData) &&
3062 !layer->HasUserData(&gThebesDisplayItemLayerUserData)) {
3063 return layer;
3068 return nullptr;
3071 static gfxSize
3072 PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale,
3073 const gfxSize& aScale)
3075 gfx3DMatrix transform =
3076 gfx3DMatrix::ScalingMatrix(aScale.width, aScale.height, 1.0);
3077 if (aFrame != aAncestorWithScale) {
3078 // aTransform is applied first, then the scale is applied to the result
3079 transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform;
3081 gfxMatrix transform2d;
3082 if (transform.CanDraw2D(&transform2d)) {
3083 return transform2d.ScaleFactors(true);
3085 return gfxSize(1.0, 1.0);
3088 gfxSize
3089 FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame* aFrame)
3091 nsIFrame* last;
3092 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
3093 last = f;
3095 if (nsLayoutUtils::IsPopup(f)) {
3096 // Don't examine ancestors of a popup. It won't make sense to check
3097 // the transform from some content inside the popup to some content
3098 // which is an ancestor of the popup.
3099 break;
3102 nsTArray<DisplayItemData*> *array =
3103 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty()));
3104 if (!array) {
3105 continue;
3108 for (uint32_t i = 0; i < array->Length(); i++) {
3109 Layer* layer = array->ElementAt(i)->mLayer;
3110 ContainerLayer* container = layer->AsContainerLayer();
3111 if (!container ||
3112 !layer->Manager()->IsWidgetLayerManager()) {
3113 continue;
3115 for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) {
3116 ThebesDisplayItemLayerUserData* data =
3117 static_cast<ThebesDisplayItemLayerUserData*>
3118 (l->GetUserData(&gThebesDisplayItemLayerUserData));
3119 if (data) {
3120 return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale));
3126 return PredictScaleForContent(aFrame, last,
3127 last->PresContext()->PresShell()->GetResolution());
3130 #ifdef MOZ_DUMP_PAINTING
3131 static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder)
3133 bool snap;
3134 nsRect appUnitBounds = aItem->GetBounds(aBuilder, &snap);
3135 gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height);
3136 bounds.ScaleInverse(aDest->AppUnitsPerDevPixel());
3138 nsRefPtr<gfxASurface> surf =
3139 gfxPlatform::GetPlatform()->CreateOffscreenSurface(gfxIntSize(bounds.width, bounds.height),
3140 gfxASurface::CONTENT_COLOR_ALPHA);
3141 surf->SetDeviceOffset(-bounds.TopLeft());
3142 nsRefPtr<gfxContext> context = new gfxContext(surf);
3143 nsRefPtr<nsRenderingContext> ctx = new nsRenderingContext();
3144 ctx->Init(aDest->DeviceContext(), context);
3146 aItem->Paint(aBuilder, ctx);
3147 DumpPaintedImage(aItem, surf);
3148 aItem->SetPainted();
3150 surf->SetDeviceOffset(gfxPoint(0, 0));
3151 aDest->ThebesContext()->SetSource(surf, bounds.TopLeft());
3152 aDest->ThebesContext()->Rectangle(bounds);
3153 aDest->ThebesContext()->Fill();
3155 #endif
3158 * A note on residual transforms:
3160 * In a transformed subtree we sometimes apply the ThebesLayer's
3161 * "residual transform" when drawing content into the ThebesLayer.
3162 * This is a translation by components in the range [-0.5,0.5) provided
3163 * by the layer system; applying the residual transform followed by the
3164 * transforms used by layer compositing ensures that the subpixel alignment
3165 * of the content of the ThebesLayer exactly matches what it would be if
3166 * we used cairo/Thebes to draw directly to the screen without going through
3167 * retained layer buffers.
3169 * The visible and valid regions of the ThebesLayer are computed without
3170 * knowing the residual transform (because we don't know what the residual
3171 * transform is going to be until we've built the layer tree!). So we have to
3172 * consider whether content painted in the range [x, xmost) might be painted
3173 * outside the visible region we computed for that content. The visible region
3174 * would be [floor(x), ceil(xmost)). The content would be rendered at
3175 * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could
3176 * indeed fall outside the computed visible region, which is not a big deal;
3177 * similar issues already arise when we snap cliprects to nearest pixels.
3178 * Note that if the rendering of the content is snapped to nearest pixels ---
3179 * which it often is --- then the content is actually rendered at
3180 * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r)
3181 * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content
3182 * always falls within the visible region we computed.
3185 /* static */ void
3186 FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
3187 gfxContext* aContext,
3188 const nsIntRegion& aRegionToDraw,
3189 const nsIntRegion& aRegionToInvalidate,
3190 void* aCallbackData)
3192 SAMPLE_LABEL("gfx", "DrawThebesLayer");
3194 nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*>
3195 (aCallbackData);
3197 FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder();
3198 NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!");
3200 if (layerBuilder->CheckDOMModified())
3201 return;
3203 nsTArray<ClippedDisplayItem> items;
3204 uint32_t commonClipCount;
3205 nsIFrame* containerLayerFrame;
3207 ThebesLayerItemsEntry* entry = layerBuilder->mThebesLayerItems.GetEntry(aLayer);
3208 NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!");
3209 items.SwapElements(entry->mItems);
3210 commonClipCount = entry->mCommonClipCount;
3211 containerLayerFrame = entry->mContainerLayerFrame;
3212 // Later after this point, due to calls to DidEndTransaction
3213 // for temporary layer managers, mThebesLayerItems can change,
3214 // so 'entry' could become invalid.
3217 if (!containerLayerFrame) {
3218 return;
3221 ThebesDisplayItemLayerUserData* userData =
3222 static_cast<ThebesDisplayItemLayerUserData*>
3223 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData));
3224 NS_ASSERTION(userData, "where did our user data go?");
3225 if (NS_GET_A(userData->mForcedBackgroundColor) > 0) {
3226 nsIntRect r = aLayer->GetVisibleRegion().GetBounds();
3227 aContext->NewPath();
3228 aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height));
3229 aContext->SetColor(gfxRGBA(userData->mForcedBackgroundColor));
3230 aContext->Fill();
3233 // make the origin of the context coincide with the origin of the
3234 // ThebesLayer
3235 gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
3236 nsIntPoint offset = GetTranslationForThebesLayer(aLayer);
3237 // Apply the residual transform if it has been enabled, to ensure that
3238 // snapping when we draw into aContext exactly matches the ideal transform.
3239 // See above for why this is OK.
3240 aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y));
3241 aContext->Scale(userData->mXScale, userData->mYScale);
3243 nsPresContext* presContext = containerLayerFrame->PresContext();
3244 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
3246 uint32_t i;
3247 // Update visible regions. We need perform visibility analysis again
3248 // because we may be asked to draw into part of a ThebesLayer that
3249 // isn't actually visible in the window (e.g., because a ThebesLayer
3250 // expanded its visible region to a rectangle internally), in which
3251 // case the mVisibleRect stored in the display item may be wrong.
3252 nsRegion visible = aRegionToDraw.ToAppUnits(appUnitsPerDevPixel);
3253 visible.MoveBy(NSIntPixelsToAppUnits(offset.x, appUnitsPerDevPixel),
3254 NSIntPixelsToAppUnits(offset.y, appUnitsPerDevPixel));
3255 visible.ScaleInverseRoundOut(userData->mXScale, userData->mYScale);
3257 for (i = items.Length(); i > 0; --i) {
3258 ClippedDisplayItem* cdi = &items[i - 1];
3260 NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == appUnitsPerDevPixel,
3261 "a thebes layer should contain items only at the same zoom");
3263 NS_ABORT_IF_FALSE(cdi->mClip.mHaveClipRect ||
3264 cdi->mClip.mRoundedClipRects.IsEmpty(),
3265 "If we have rounded rects, we must have a clip rect");
3267 if (!cdi->mClip.mHaveClipRect ||
3268 (cdi->mClip.mRoundedClipRects.IsEmpty() &&
3269 cdi->mClip.mClipRect.Contains(visible.GetBounds()))) {
3270 cdi->mItem->RecomputeVisibility(builder, &visible);
3271 continue;
3274 // Do a little dance to account for the fact that we're clipping
3275 // to cdi->mClipRect
3276 nsRegion clipped;
3277 clipped.And(visible, cdi->mClip.mClipRect);
3278 nsRegion finalClipped = clipped;
3279 cdi->mItem->RecomputeVisibility(builder, &finalClipped);
3280 // If we have rounded clip rects, don't subtract from the visible
3281 // region since we aren't displaying everything inside the rect.
3282 if (cdi->mClip.mRoundedClipRects.IsEmpty()) {
3283 nsRegion removed;
3284 removed.Sub(clipped, finalClipped);
3285 nsRegion newVisible;
3286 newVisible.Sub(visible, removed);
3287 // Don't let the visible region get too complex.
3288 if (newVisible.GetNumRects() <= 15) {
3289 visible = newVisible;
3292 if (!cdi->mClip.IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) {
3293 cdi->mClip.RemoveRoundedCorners();
3297 nsRefPtr<nsRenderingContext> rc = new nsRenderingContext();
3298 rc->Init(presContext->DeviceContext(), aContext);
3300 Clip currentClip;
3301 bool setClipRect = false;
3303 for (i = 0; i < items.Length(); ++i) {
3304 ClippedDisplayItem* cdi = &items[i];
3306 if (cdi->mItem->GetVisibleRect().IsEmpty())
3307 continue;
3309 // If the new desired clip state is different from the current state,
3310 // update the clip.
3311 if (setClipRect != cdi->mClip.mHaveClipRect ||
3312 (cdi->mClip.mHaveClipRect && cdi->mClip != currentClip)) {
3313 if (setClipRect) {
3314 aContext->Restore();
3316 setClipRect = cdi->mClip.mHaveClipRect;
3317 if (setClipRect) {
3318 currentClip = cdi->mClip;
3319 aContext->Save();
3320 NS_ASSERTION(commonClipCount < 100,
3321 "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong.");
3322 currentClip.ApplyTo(aContext, presContext, commonClipCount);
3326 if (cdi->mInactiveLayerManager) {
3327 PaintInactiveLayer(builder, cdi->mInactiveLayerManager, cdi->mItem, aContext, rc);
3328 } else {
3329 nsIFrame* frame = cdi->mItem->GetUnderlyingFrame();
3330 if (frame) {
3331 frame->AddStateBits(NS_FRAME_PAINTED_THEBES);
3333 #ifdef MOZ_DUMP_PAINTING
3335 if (gfxUtils::sDumpPainting) {
3336 DebugPaintItem(rc, cdi->mItem, builder);
3337 } else {
3338 #else
3340 #endif
3341 cdi->mItem->Paint(builder, rc);
3345 if (layerBuilder->CheckDOMModified())
3346 break;
3350 ThebesLayerItemsEntry* entry =
3351 layerBuilder->mThebesLayerItems.GetEntry(aLayer);
3352 items.SwapElements(entry->mItems);
3355 if (setClipRect) {
3356 aContext->Restore();
3359 FlashPaint(aContext);
3360 if (!aRegionToInvalidate.IsEmpty()) {
3361 aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds());
3365 bool
3366 FrameLayerBuilder::CheckDOMModified()
3368 if (!mRootPresContext ||
3369 mInitialDOMGeneration == mRootPresContext->GetDOMGeneration())
3370 return false;
3371 if (mDetectedDOMModification) {
3372 // Don't spam the console with extra warnings
3373 return true;
3375 mDetectedDOMModification = true;
3376 // Painting is not going to complete properly. There's not much
3377 // we can do here though. Invalidating the window to get another repaint
3378 // is likely to lead to an infinite repaint loop.
3379 NS_WARNING("Detected DOM modification during paint, bailing out!");
3380 return true;
3383 #ifdef MOZ_DUMP_PAINTING
3384 /* static */ void
3385 FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bool aDumpHtml)
3387 aManager->Dump(aFile, "", aDumpHtml);
3389 #endif
3391 FrameLayerBuilder::Clip::Clip(const Clip& aOther, nsDisplayItem* aClipItem)
3392 : mRoundedClipRects(aOther.mRoundedClipRects),
3393 mHaveClipRect(true)
3395 nsDisplayItem::Type type = aClipItem->GetType();
3396 NS_ABORT_IF_FALSE(type == nsDisplayItem::TYPE_CLIP ||
3397 type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT,
3398 "unexpected display item type");
3399 nsDisplayClip* item = static_cast<nsDisplayClip*>(aClipItem);
3400 // Always intersect with mClipRect, even if we're going to add a
3401 // rounded rect.
3402 if (aOther.mHaveClipRect) {
3403 mClipRect.IntersectRect(aOther.mClipRect, item->GetClipRect());
3404 } else {
3405 mClipRect = item->GetClipRect();
3408 if (type == nsDisplayItem::TYPE_CLIP_ROUNDED_RECT) {
3409 RoundedRect *rr = mRoundedClipRects.AppendElement();
3410 if (rr) {
3411 rr->mRect = item->GetClipRect();
3412 static_cast<nsDisplayClipRoundedRect*>(item)->GetRadii(rr->mRadii);
3416 // FIXME: Optimize away excess rounded rectangles due to the new addition.
3419 void
3420 FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext,
3421 nsPresContext* aPresContext,
3422 uint32_t aBegin, uint32_t aEnd)
3424 int32_t A2D = aPresContext->AppUnitsPerDevPixel();
3425 ApplyRectTo(aContext, A2D);
3426 ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd);
3429 void
3430 FrameLayerBuilder::Clip::ApplyRectTo(gfxContext* aContext, int32_t A2D) const
3432 aContext->NewPath();
3433 gfxRect clip = nsLayoutUtils::RectToGfxRect(mClipRect, A2D);
3434 aContext->Rectangle(clip, true);
3435 aContext->Clip();
3438 void
3439 FrameLayerBuilder::Clip::ApplyRoundedRectsTo(gfxContext* aContext,
3440 int32_t A2D,
3441 uint32_t aBegin, uint32_t aEnd) const
3443 aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
3445 for (uint32_t i = aBegin; i < aEnd; ++i) {
3446 AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[i]);
3447 aContext->Clip();
3451 void
3452 FrameLayerBuilder::Clip::DrawRoundedRectsTo(gfxContext* aContext,
3453 int32_t A2D,
3454 uint32_t aBegin, uint32_t aEnd) const
3456 aEnd = std::min<uint32_t>(aEnd, mRoundedClipRects.Length());
3458 if (aEnd - aBegin == 0)
3459 return;
3461 // If there is just one rounded rect we can just fill it, if there are more then we
3462 // must clip the rest to get the intersection of clips
3463 ApplyRoundedRectsTo(aContext, A2D, aBegin, aEnd - 1);
3464 AddRoundedRectPathTo(aContext, A2D, mRoundedClipRects[aEnd - 1]);
3465 aContext->Fill();
3468 void
3469 FrameLayerBuilder::Clip::AddRoundedRectPathTo(gfxContext* aContext,
3470 int32_t A2D,
3471 const RoundedRect &aRoundRect) const
3473 gfxCornerSizes pixelRadii;
3474 nsCSSRendering::ComputePixelRadii(aRoundRect.mRadii, A2D, &pixelRadii);
3476 gfxRect clip = nsLayoutUtils::RectToGfxRect(aRoundRect.mRect, A2D);
3477 clip.Round();
3478 clip.Condition();
3480 aContext->NewPath();
3481 aContext->RoundedRectangle(clip, pixelRadii);
3484 nsRect
3485 FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const
3487 nsRect r = aRect;
3488 if (mHaveClipRect) {
3489 r.IntersectRect(r, mClipRect);
3491 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
3492 i < iEnd; ++i) {
3493 const Clip::RoundedRect &rr = mRoundedClipRects[i];
3494 nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r);
3495 r = rgn.GetLargestRectangle();
3497 return r;
3500 // Test if (aXPoint, aYPoint) is in the ellipse with center (aXCenter, aYCenter)
3501 // and radii aXRadius, aYRadius.
3502 bool IsInsideEllipse(nscoord aXRadius, nscoord aXCenter, nscoord aXPoint,
3503 nscoord aYRadius, nscoord aYCenter, nscoord aYPoint)
3505 float scaledX = float(aXPoint - aXCenter) / float(aXRadius);
3506 float scaledY = float(aYPoint - aYCenter) / float(aYRadius);
3507 return scaledX * scaledX + scaledY * scaledY < 1.0f;
3510 bool
3511 FrameLayerBuilder::Clip::IsRectClippedByRoundedCorner(const nsRect& aRect) const
3513 if (mRoundedClipRects.IsEmpty())
3514 return false;
3516 nsRect rect;
3517 rect.IntersectRect(aRect, NonRoundedIntersection());
3518 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
3519 i < iEnd; ++i) {
3520 const Clip::RoundedRect &rr = mRoundedClipRects[i];
3521 // top left
3522 if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X] &&
3523 rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y]) {
3524 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_LEFT_X],
3525 rr.mRect.x + rr.mRadii[NS_CORNER_TOP_LEFT_X],
3526 rect.x,
3527 rr.mRadii[NS_CORNER_TOP_LEFT_Y],
3528 rr.mRect.y + rr.mRadii[NS_CORNER_TOP_LEFT_Y],
3529 rect.y)) {
3530 return true;
3533 // top right
3534 if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X] &&
3535 rect.y < rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y]) {
3536 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_TOP_RIGHT_X],
3537 rr.mRect.XMost() - rr.mRadii[NS_CORNER_TOP_RIGHT_X],
3538 rect.XMost(),
3539 rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
3540 rr.mRect.y + rr.mRadii[NS_CORNER_TOP_RIGHT_Y],
3541 rect.y)) {
3542 return true;
3545 // bottom left
3546 if (rect.x < rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X] &&
3547 rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y]) {
3548 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
3549 rr.mRect.x + rr.mRadii[NS_CORNER_BOTTOM_LEFT_X],
3550 rect.x,
3551 rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
3552 rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_LEFT_Y],
3553 rect.YMost())) {
3554 return true;
3557 // bottom right
3558 if (rect.XMost() > rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X] &&
3559 rect.YMost() > rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y]) {
3560 if (!IsInsideEllipse(rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
3561 rr.mRect.XMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_X],
3562 rect.XMost(),
3563 rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
3564 rr.mRect.YMost() - rr.mRadii[NS_CORNER_BOTTOM_RIGHT_Y],
3565 rect.YMost())) {
3566 return true;
3570 return false;
3573 nsRect
3574 FrameLayerBuilder::Clip::NonRoundedIntersection() const
3576 NS_ASSERTION(mHaveClipRect, "Must have a clip rect!");
3577 nsRect result = mClipRect;
3578 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
3579 i < iEnd; ++i) {
3580 result.IntersectRect(result, mRoundedClipRects[i].mRect);
3582 return result;
3585 nsRect
3586 FrameLayerBuilder::Clip::ApplyNonRoundedIntersection(const nsRect& aRect) const
3588 if (!mHaveClipRect) {
3589 return aRect;
3592 nsRect result = aRect.Intersect(mClipRect);
3593 for (uint32_t i = 0, iEnd = mRoundedClipRects.Length();
3594 i < iEnd; ++i) {
3595 result.Intersect(mRoundedClipRects[i].mRect);
3597 return result;
3600 void
3601 FrameLayerBuilder::Clip::RemoveRoundedCorners()
3603 if (mRoundedClipRects.IsEmpty())
3604 return;
3606 mClipRect = NonRoundedIntersection();
3607 mRoundedClipRects.Clear();
3610 static void
3611 AccumulateRectDifference(const nsRect& aR1, const nsRect& aR2, nsRegion* aOut)
3613 if (aR1.IsEqualInterior(aR2))
3614 return;
3615 nsRegion r;
3616 r.Xor(aR1, aR2);
3617 aOut->Or(*aOut, r);
3620 void
3621 FrameLayerBuilder::Clip::AddOffsetAndComputeDifference(const nsPoint& aOffset,
3622 const nsRect& aBounds,
3623 const Clip& aOther,
3624 const nsRect& aOtherBounds,
3625 nsRegion* aDifference)
3627 if (mHaveClipRect != aOther.mHaveClipRect ||
3628 mRoundedClipRects.Length() != aOther.mRoundedClipRects.Length()) {
3629 aDifference->Or(*aDifference, aBounds);
3630 aDifference->Or(*aDifference, aOtherBounds);
3631 return;
3633 if (mHaveClipRect) {
3634 AccumulateRectDifference((mClipRect + aOffset).Intersect(aBounds),
3635 aOther.mClipRect.Intersect(aOtherBounds),
3636 aDifference);
3638 for (uint32_t i = 0; i < mRoundedClipRects.Length(); ++i) {
3639 if (mRoundedClipRects[i] + aOffset != aOther.mRoundedClipRects[i]) {
3640 // The corners make it tricky so we'll just add both rects here.
3641 aDifference->Or(*aDifference, mRoundedClipRects[i].mRect.Intersect(aBounds));
3642 aDifference->Or(*aDifference, aOther.mRoundedClipRects[i].mRect.Intersect(aOtherBounds));
3647 gfxRect
3648 CalculateBounds(const nsTArray<FrameLayerBuilder::Clip::RoundedRect>& aRects, int32_t A2D)
3650 nsRect bounds = aRects[0].mRect;
3651 for (uint32_t i = 1; i < aRects.Length(); ++i) {
3652 bounds.UnionRect(bounds, aRects[i].mRect);
3655 return nsLayoutUtils::RectToGfxRect(bounds, A2D);
3658 static void
3659 SetClipCount(ThebesDisplayItemLayerUserData* aThebesData,
3660 uint32_t aClipCount)
3662 if (aThebesData) {
3663 aThebesData->mMaskClipCount = aClipCount;
3667 void
3668 ContainerState::SetupMaskLayer(Layer *aLayer, const FrameLayerBuilder::Clip& aClip,
3669 uint32_t aRoundedRectClipCount)
3671 // if the number of clips we are going to mask has decreased, then aLayer might have
3672 // cached graphics which assume the existence of a soon-to-be non-existent mask layer
3673 // in that case, invalidate the whole layer.
3674 ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer);
3675 if (thebesData &&
3676 aRoundedRectClipCount < thebesData->mMaskClipCount) {
3677 ThebesLayer* thebes = aLayer->AsThebesLayer();
3678 thebes->InvalidateRegion(thebes->GetValidRegion().GetBounds());
3681 // don't build an unnecessary mask
3682 nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds();
3683 if (aClip.mRoundedClipRects.IsEmpty() ||
3684 aRoundedRectClipCount == 0 ||
3685 layerBounds.IsEmpty()) {
3686 SetClipCount(thebesData, 0);
3687 return;
3690 // check if we can re-use the mask layer
3691 nsRefPtr<ImageLayer> maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer);
3692 MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer);
3694 MaskLayerUserData newData;
3695 newData.mRoundedClipRects.AppendElements(aClip.mRoundedClipRects);
3696 if (aRoundedRectClipCount < newData.mRoundedClipRects.Length()) {
3697 newData.mRoundedClipRects.TruncateLength(aRoundedRectClipCount);
3699 newData.mScaleX = mParameters.mXScale;
3700 newData.mScaleY = mParameters.mYScale;
3702 if (*userData == newData) {
3703 aLayer->SetMaskLayer(maskLayer);
3704 SetClipCount(thebesData, aRoundedRectClipCount);
3705 return;
3708 // calculate a more precise bounding rect
3709 const int32_t A2D = mContainerFrame->PresContext()->AppUnitsPerDevPixel();
3710 gfxRect boundingRect = CalculateBounds(newData.mRoundedClipRects, A2D);
3711 boundingRect.Scale(mParameters.mXScale, mParameters.mYScale);
3713 uint32_t maxSize = mManager->GetMaxTextureSize();
3714 NS_ASSERTION(maxSize > 0, "Invalid max texture size");
3715 nsIntSize surfaceSize(std::min<int32_t>(boundingRect.Width(), maxSize),
3716 std::min<int32_t>(boundingRect.Height(), maxSize));
3718 // maskTransform is applied to the clip when it is painted into the mask (as a
3719 // component of imageTransform), and its inverse used when the mask is used for
3720 // masking.
3721 // It is the transform from the masked layer's space to mask space
3722 gfxMatrix maskTransform;
3723 maskTransform.Scale(float(surfaceSize.width)/float(boundingRect.Width()),
3724 float(surfaceSize.height)/float(boundingRect.Height()));
3725 maskTransform.Translate(-boundingRect.TopLeft());
3726 // imageTransform is only used when the clip is painted to the mask
3727 gfxMatrix imageTransform = maskTransform;
3728 imageTransform.Scale(mParameters.mXScale, mParameters.mYScale);
3730 nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey(
3731 new MaskLayerImageCache::MaskLayerImageKey());
3733 // copy and transform the rounded rects
3734 for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) {
3735 newKey->mRoundedClipRects.AppendElement(
3736 MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i],
3737 mContainerFrame->PresContext()));
3738 newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform);
3741 const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey;
3743 // check to see if we can reuse a mask image
3744 nsRefPtr<ImageContainer> container =
3745 GetMaskLayerImageCache()->FindImageFor(&lookupKey);
3747 if (!container) {
3748 // no existing mask image, so build a new one
3749 nsRefPtr<gfxASurface> surface =
3750 aLayer->Manager()->CreateOptimalMaskSurface(surfaceSize);
3752 // fail if we can't get the right surface
3753 if (!surface || surface->CairoStatus()) {
3754 NS_WARNING("Could not create surface for mask layer.");
3755 SetClipCount(thebesData, 0);
3756 return;
3759 nsRefPtr<gfxContext> context = new gfxContext(surface);
3760 context->Multiply(imageTransform);
3762 // paint the clipping rects with alpha to create the mask
3763 context->SetColor(gfxRGBA(1, 1, 1, 1));
3764 aClip.DrawRoundedRectsTo(context, A2D, 0, aRoundedRectClipCount);
3766 // build the image and container
3767 container = aLayer->Manager()->CreateImageContainer();
3768 NS_ASSERTION(container, "Could not create image container for mask layer.");
3769 static const ImageFormat format = CAIRO_SURFACE;
3770 nsRefPtr<Image> image = container->CreateImage(&format, 1);
3771 NS_ASSERTION(image, "Could not create image container for mask layer.");
3772 CairoImage::Data data;
3773 data.mSurface = surface;
3774 data.mSize = surfaceSize;
3775 static_cast<CairoImage*>(image.get())->SetData(data);
3776 container->SetCurrentImageInTransaction(image);
3778 GetMaskLayerImageCache()->PutImage(newKey.forget(), container);
3781 maskLayer->SetContainer(container);
3783 gfx3DMatrix matrix = gfx3DMatrix::From2D(maskTransform.Invert());
3784 matrix.Translate(gfxPoint3D(mParameters.mOffset.x, mParameters.mOffset.y, 0));
3785 maskLayer->SetBaseTransform(matrix);
3787 // save the details of the clip in user data
3788 userData->mScaleX = newData.mScaleX;
3789 userData->mScaleY = newData.mScaleY;
3790 userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects);
3791 userData->mImageKey = lookupKey;
3793 aLayer->SetMaskLayer(maskLayer);
3794 SetClipCount(thebesData, aRoundedRectClipCount);
3795 return;
3798 } // namespace mozilla