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"
14 #include "BasicLayers.h"
15 #include "nsSubDocumentFrame.h"
16 #include "nsCSSRendering.h"
17 #include "nsCSSFrameConstructor.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"
28 #include "mozilla/gfx/Tools.h"
30 #include "nsAnimationManager.h"
31 #include "nsTransitionManager.h"
36 //#define DEBUG_INVALIDATIONS
37 //#define DEBUG_DISPLAY_ITEM_DATA
40 using namespace mozilla::layers
;
41 using namespace mozilla::gfx
;
45 FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData
* aParent
, uint32_t aKey
,
46 Layer
* aLayer
, LayerState aLayerState
, uint32_t aGeneration
)
50 , mDisplayItemKey(aKey
)
51 , mContainerLayerGeneration(aGeneration
)
52 , mLayerState(aLayerState
)
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
;
69 mContainerLayerGeneration
= toCopy
.mContainerLayerGeneration
;
70 mLayerState
= toCopy
.mLayerState
;
75 FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame
* aFrame
)
77 mFrameList
.AppendElement(aFrame
);
79 nsTArray
<DisplayItemData
*> *array
=
80 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty()));
82 array
= new nsTArray
<DisplayItemData
*>();
83 aFrame
->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array
);
85 array
->AppendElement(this);
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);
101 FrameLayerBuilder::DisplayItemData::UpdateContents(Layer
* aLayer
, LayerState aState
,
102 uint32_t aContainerLayerGeneration
,
103 nsDisplayItem
* aItem
/* = nullptr */)
107 mInactiveManager
= nullptr;
108 mLayerState
= aState
;
109 mContainerLayerGeneration
= aContainerLayerGeneration
;
111 mClip
.mHaveClipRect
= false;
112 mClip
.mRoundedClipRects
.Clear();
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
) {
145 nsTArray
<DisplayItemData
*> *array
=
146 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(frame
->Properties().Get(LayerManagerDataProperty()));
147 array
->RemoveElement(this);
152 FrameLayerBuilder::DisplayItemData::GetFrameListChanges(nsDisplayItem
* aOther
,
153 nsTArray
<nsIFrame
*>& aOut
)
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
{
177 LayerManagerData(LayerManager
*aManager
)
178 : mLayerManager(aManager
)
179 #ifdef DEBUG_DISPLAY_ITEM_DATA
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
;
197 mDisplayItems
.EnumerateEntries(
198 FrameLayerBuilder::DumpDisplayItemDataForFrame
, (void*)prefix
.get());
203 * Tracks which frames have layers associated with them.
205 LayerManager
*mLayerManager
;
206 #ifdef DEBUG_DISPLAY_ITEM_DATA
207 LayerManagerData
*mParent
;
209 nsTHashtable
<nsRefPtrHashKey
<FrameLayerBuilder::DisplayItemData
> > mDisplayItems
;
210 bool mInvalidateAllLayers
;
214 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame
* aFrame
)
216 FrameProperties props
= aFrame
->Properties();
217 props
.Delete(LayerManagerDataProperty());
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
238 class ContainerState
{
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
261 mSnappingEnabled
= aManager
->IsSnappingEffectiveTransforms() &&
262 !mParameters
.AllowResidualTranslation();
263 mRecycledMaskImageLayers
.Init();
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
,
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
);
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
{
344 mActiveScrolledRoot(nullptr), mLayer(nullptr),
345 mIsSolidColorInVisibleRegion(false),
346 mNeedComponentAlpha(false),
347 mForceTransparentSurface(false),
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
;
420 * If mIsSolidColorInVisibleRegion is true, this is the color of the visible
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
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
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
,
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
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
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
580 nsIntRegion mInvalidThebesContent
;
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,
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
601 ThebesDisplayItemLayerUserData() :
603 mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
604 mXScale(1.f
), mYScale(1.f
),
605 mAppUnitsPerDevPixel(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) {}
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
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
740 FrameLayerBuilder::Shutdown()
742 if (gMaskLayerImageCache
) {
743 delete gMaskLayerImageCache
;
744 gMaskLayerImageCache
= nullptr;
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);
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));
780 FrameLayerBuilder::DisplayItemData
*
781 FrameLayerBuilder::GetDisplayItemData(nsIFrame
* aFrame
, uint32_t aKey
)
783 nsTArray
<DisplayItemData
*> *array
=
784 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
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
) {
798 AppendToString(nsACString
& s
, const nsIntRect
& r
,
799 const char* pfx
="", const char* sfx
="")
802 s
+= nsPrintfCString(
803 "(x=%d, y=%d, w=%d, h=%d)",
804 r
.x
, r
.y
, r
.width
, r
.height
);
809 AppendToString(nsACString
& s
, const nsIntRegion
& r
,
810 const char* pfx
="", const char* sfx
="")
814 nsIntRegionRectIterator
it(r
);
816 while (const nsIntRect
* sr
= it
.Next()) {
817 AppendToString(s
, *sr
) += "; ";
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.
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
841 AppendToString(str
, rgn
);
842 printf("Invalidating layer %p: %s\n", aLayer
, str
.get());
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
);
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.
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
);
914 for (uint32_t i
= 0; i
< array
->Length(); ++i
) {
915 DisplayItemData
* data
= array
->ElementAt(i
);
917 ThebesLayer
* t
= data
->mLayer
->AsThebesLayer();
919 ThebesDisplayItemLayerUserData
* thebesData
=
920 static_cast<ThebesDisplayItemLayerUserData
*>(t
->GetUserData(&gThebesDisplayItemLayerUserData
));
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
);
934 sDestroyedFrame
= NULL
;
938 FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager
* aManager
)
940 mRetainingManager
= aManager
;
941 LayerManagerData
* data
= static_cast<LayerManagerData
*>
942 (aManager
->GetUserData(&gLayerManagerUserData
));
944 mInvalidateAllLayers
= data
->mInvalidateAllLayers
;
946 data
= new LayerManagerData(aManager
);
947 aManager
->SetUserData(&gLayerManagerUserData
, data
);
952 FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem
* aItem
, Layer
* aLayer
)
954 if (!mRetainingManager
) {
958 DisplayItemData
* data
= GetDisplayItemDataForManager(aItem
, aLayer
->Manager());
959 NS_ASSERTION(data
, "Must have already stored data for this item!");
960 data
->mOptLayer
= aLayer
;
964 FrameLayerBuilder::DidEndTransaction()
966 GetMaskLayerImageCache()->Sweep();
970 FrameLayerBuilder::WillEndTransaction()
972 if (!mRetainingManager
) {
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
,
989 DisplayItemData
* data
= aEntry
->GetKey();
991 // This item was visible, but isn't anymore.
992 FrameLayerBuilder
* layerBuilder
= static_cast<FrameLayerBuilder
*>(aUserArg
);
994 ThebesLayer
* t
= data
->mLayer
->AsThebesLayer();
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
);
999 InvalidatePostTransformRegion(t
,
1000 data
->mGeometry
->ComputeInvalidationRegion(),
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
,
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
) {
1025 layerState
= "LAYER_NONE"; break;
1026 case LAYER_INACTIVE
:
1027 layerState
= "LAYER_INACTIVE"; break;
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;
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());
1055 printf("%s", str
.get());
1057 if (data
->mInactiveManager
) {
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());
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()));
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
) {
1087 FrameLayerBuilder::HasRetainedDataFor(nsIFrame
* aFrame
, uint32_t aDisplayItemKey
)
1089 nsTArray
<DisplayItemData
*> *array
=
1090 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
1092 for (uint32_t i
= 0; i
< array
->Length(); i
++) {
1093 if (array
->ElementAt(i
)->mDisplayItemKey
== aDisplayItemKey
) {
1102 FrameLayerBuilder::IterateRetainedDataFor(nsIFrame
* aFrame
, DisplayItemDataCallback aCallback
)
1104 nsTArray
<DisplayItemData
*> *array
=
1105 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
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
1123 if (!mRetainingManager
|| mInvalidateAllLayers
)
1126 DisplayItemData
*data
= GetDisplayItemData(aFrame
, aDisplayItemKey
);
1128 if (data
&& data
->mLayer
->Manager() == mRetainingManager
) {
1135 FrameLayerBuilder::GetOldLayerFor(nsDisplayItem
* aItem
,
1136 nsDisplayItemGeometry
** aOldGeometry
,
1138 nsTArray
<nsIFrame
*>* aChangedFrames
,
1141 uint32_t key
= aItem
->GetPerFrameKey();
1142 nsIFrame
* frame
= aItem
->GetUnderlyingFrame();
1145 DisplayItemData
* oldData
= GetOldLayerForFrame(frame
, key
);
1148 *aOldGeometry
= oldData
->mGeometry
.get();
1151 *aOldClip
= &oldData
->mClip
;
1153 if (aChangedFrames
) {
1154 oldData
->GetFrameListChanges(aItem
, *aChangedFrames
);
1157 *aIsInvalid
= oldData
->mIsInvalid
;
1159 return oldData
->mLayer
;
1167 FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame
* aFrame
, uint32_t aDisplayItemKey
)
1169 nsTArray
<DisplayItemData
*> *array
=
1170 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
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
;
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
;
1193 layer
->SetClipRect(nullptr);
1194 layer
->SetMaskLayer(nullptr);
1196 // Create a new layer
1197 layer
= mManager
->CreateColorLayer();
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
;
1217 layer
->SetClipRect(nullptr);
1218 layer
->SetMaskLayer(nullptr);
1220 // Create a new layer
1221 layer
= mManager
->CreateImageLayer();
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
);
1239 mRecycledMaskImageLayers
.Remove(aLayer
);
1240 // XXX if we use clip on mask layers, null it out here
1242 // Create a new layer
1243 result
= mManager
->CreateImageLayer();
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.
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
) {
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
));
1282 ResetScrollPositionForLayerPixelAlignment(const nsIFrame
* aActiveScrolledRoot
)
1284 nsIScrollableFrame
* sf
= nsLayoutUtils::GetScrollableFrameFor(aActiveScrolledRoot
);
1286 sf
->ResetScrollPositionForLayerPixelAlignment();
1291 InvalidateEntireThebesLayer(ThebesLayer
* aLayer
, const nsIFrame
* aActiveScrolledRoot
)
1293 #ifdef DEBUG_INVALIDATIONS
1294 printf("Invalidating entire layer %p\n", aLayer
);
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;
1312 if (mNextFreeRecycledThebesLayer
< mRecycledThebesLayers
.Length()) {
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;
1341 if (!data
->mRegionToInvalidate
.IsEmpty()) {
1342 #ifdef DEBUG_INVALIDATIONS
1343 printf("Invalidating deleted frame content from layer %p\n", layer
.get());
1345 layer
->InvalidateRegion(data
->mRegionToInvalidate
);
1346 #ifdef DEBUG_INVALIDATIONS
1348 AppendToString(str
, data
->mRegionToInvalidate
);
1349 printf("Invalidating layer %p: %s\n", layer
.get(), str
.get());
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.
1358 // Create a new thebes layer
1359 layer
= mManager
->CreateThebesLayer();
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;
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
;
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
;
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.
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
1428 if (aItem
->GetType() == nsDisplayItem::TYPE_ZOOM
) {
1429 return static_cast<nsDisplayZoom
*>(aItem
)->GetParentAppUnitsPerDevPixel();
1431 return aItem
->GetUnderlyingFrame()->PresContext()->AppUnitsPerDevPixel();
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)
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
))
1458 nsIntRegion rgn
= aLayer
->GetVisibleRegion();
1459 if (!visibleRect
.Contains(rgn
.GetBounds())) {
1460 rgn
.And(rgn
, visibleRect
);
1461 aLayer
->SetVisibleRegion(rgn
);
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
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
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
;
1498 nsRect bounds
= item
->GetBounds(mBuilder
, &snap
);
1499 if (snap
&& mSnappingEnabled
) {
1500 nsIntRect snappedBounds
= ScaleToNearestPixels(bounds
);
1501 if (!snappedBounds
.Intersects(deviceRect
))
1504 if (!snappedBounds
.Contains(deviceRect
))
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
))
1513 if (!bounds
.Contains(appUnitRect
))
1518 if (item
->IsUniform(mBuilder
, &color
) && NS_GET_A(color
) == 255)
1525 return NS_RGBA(0,0,0,0);
1529 ContainerState::ThebesLayerData::UpdateCommonClipCount(
1530 const FrameLayerBuilder::Clip
& aCurrentClip
)
1532 if (mCommonClipCount
>= 0) {
1533 int32_t end
= std::min
<int32_t>(aCurrentClip
.mRoundedClipRects
.Length(),
1535 int32_t clipCount
= 0;
1536 for (; clipCount
< end
; ++clipCount
) {
1537 if (mItemClip
.mRoundedClipRects
[clipCount
] !=
1538 aCurrentClip
.mRoundedClipRects
[clipCount
]) {
1542 mCommonClipCount
= clipCount
;
1543 NS_ASSERTION(mItemClip
.mRoundedClipRects
.Length() >= uint32_t(mCommonClipCount
),
1544 "Inconsistent common clip count.");
1546 // first item in the layer
1547 mCommonClipCount
= aCurrentClip
.mRoundedClipRects
.Length();
1551 already_AddRefed
<ImageContainer
>
1552 ContainerState::ThebesLayerData::CanOptimizeImageLayer(nsDisplayListBuilder
* aBuilder
)
1558 return mImage
->GetContainer(mLayer
->Manager(), aBuilder
);
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
);
1588 mLayerBuilder
->StoreOptimizedLayerForFrame(data
->mImage
,
1591 nsRefPtr
<ColorLayer
> colorLayer
= CreateOrRecycleColorLayer(data
->mLayer
);
1592 colorLayer
->SetIsFixedPosition(data
->mLayer
->GetIsFixedPosition());
1593 colorLayer
->SetColor(data
->mSolidColor
);
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
);
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());
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);
1651 backgroundColor
= FindOpaqueBackgroundColorFor(lastIndex
);
1652 if (NS_GET_A(backgroundColor
) == 255) {
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
;
1677 // mask layer for image and color layers
1678 SetupMaskLayer(layer
, data
->mItemClip
);
1681 if (isOpaque
&& !data
->mForceTransparentSurface
) {
1682 flags
= Layer::CONTENT_OPAQUE
;
1683 } else if (data
->mNeedComponentAlpha
) {
1684 flags
= Layer::CONTENT_COMPONENT_ALPHA
;
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
,
1704 nextData
->mDrawAboveRegion
.SimplifyOutward(4);
1707 mThebesLayerDataStack
.RemoveElementAt(lastIndex
);
1711 SuppressComponentAlpha(nsDisplayListBuilder
* aBuilder
,
1712 nsDisplayItem
* aItem
,
1713 const nsRect
& aComponentAlphaBounds
)
1715 const nsRegion
* windowTransparentRegion
= aBuilder
->GetFinalTransparentRegion();
1716 if (!windowTransparentRegion
|| windowTransparentRegion
->IsEmpty())
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())
1726 for (nsIFrame
* t
= f
; t
; t
= t
->GetParent()) {
1727 if (t
->IsTransformed())
1731 return windowTransparentRegion
->Intersects(aComponentAlphaBounds
);
1735 WindowHasTransparency(nsDisplayListBuilder
* aBuilder
)
1737 const nsRegion
* windowTransparentRegion
= aBuilder
->GetFinalTransparentRegion();
1738 return windowTransparentRegion
&& !windowTransparentRegion
->IsEmpty();
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
);
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");
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).
1795 nsRect bounds
= aItem
->GetBounds(aState
->mBuilder
, &snap
);
1796 if (!aState
->ScaleToInsidePixels(bounds
, snap
).Contains(aVisibleRect
)) {
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
);
1810 mIsSolidColorInVisibleRegion
= false;
1813 mIsSolidColorInVisibleRegion
= false;
1816 mVisibleRegion
.Or(mVisibleRegion
, aVisibleRect
);
1817 mVisibleRegion
.SimplifyOutward(4);
1818 mDrawRegion
.Or(mDrawRegion
, aDrawRect
);
1819 mDrawRegion
.SimplifyOutward(4);
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.
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();
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
)
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
)) {
1886 if (data
->mActiveScrolledRoot
== aActiveScrolledRoot
) {
1887 lowestUsableLayerWithScrolledRoot
= i
;
1888 if (topmostLayerWithScrolledRoot
< 0) {
1889 topmostLayerWithScrolledRoot
= i
;
1892 if (data
->mDrawRegion
.Intersects(aVisibleRect
))
1895 if (topmostLayerWithScrolledRoot
< 0) {
1897 for (; i
>= 0; --i
) {
1898 ThebesLayerData
* data
= mThebesLayerDataStack
[i
];
1899 if (data
->mActiveScrolledRoot
== aActiveScrolledRoot
) {
1900 topmostLayerWithScrolledRoot
= i
;
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
;
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
1940 DumpPaintedImage(nsDisplayItem
* aItem
, gfxASurface
* aSurf
)
1942 nsCString
string(aItem
->Name());
1944 string
.AppendInt((uint64_t)aItem
);
1945 fprintf(gfxUtils::sDumpPaintFile
, "array[\"%s\"]=\"", string
.BeginReading());
1946 aSurf
->DumpAsDataURL(gfxUtils::sDumpPaintFile
);
1947 fprintf(gfxUtils::sDumpPaintFile
, "\";");
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
);
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();
1983 basic
->EndTransaction(FrameLayerBuilder::DrawThebesLayer
, aBuilder
);
1985 FrameLayerBuilder
*builder
= static_cast<FrameLayerBuilder
*>(basic
->GetUserData(&gLayerManagerLayerBuilder
));
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
);
2000 aItem
->SetPainted();
2006 * Chooses a single active scrolled root for the entire display list, used
2007 * when we are flattening layers.
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
)) {
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
) {
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
) {
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.
2056 ContainerState::ProcessDisplayItems(const nsDisplayList
& aList
,
2057 FrameLayerBuilder::Clip
& aClip
,
2059 const nsIFrame
* aForceActiveScrolledRoot
)
2061 SAMPLE_LABEL("ContainerState", "ProcessDisplayItems");
2063 const nsIFrame
* lastActiveScrolledRoot
= nullptr;
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
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
);
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);
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
;
2112 const nsIFrame
* activeScrolledRoot
;
2113 if (aFlags
& NO_COMPONENT_ALPHA
) {
2114 forceInactive
= true;
2115 activeScrolledRoot
= lastActiveScrolledRoot
;
2116 isFixed
= mBuilder
->IsFixedItem(item
, nullptr, activeScrolledRoot
);
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()) ||
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
) {
2153 // Just use its layer.
2154 nsRefPtr
<Layer
> ownLayer
= item
->BuildLayer(mBuilder
, mManager
, mParameters
);
2159 NS_ASSERTION(!ownLayer
->AsThebesLayer(),
2160 "Should never have created a dedicated Thebes layer!");
2163 if (item
->IsInvalid(invalid
)) {
2164 ownLayer
->SetInvalidRectToVisibleRegion();
2167 // If it's not a ContainerLayer, we need to apply the scale transform
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
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();
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
,
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
,
2244 layerState
, topLeft
,
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
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
)) {
2276 aCombined
= aOldClip
->NonRoundedIntersection();
2277 aCombined
.MoveBy(aShift
);
2278 aCombined
.Or(aCombined
, aClip
.NonRoundedIntersection());
2280 aCombined
= aClip
.NonRoundedIntersection();
2286 ContainerState::InvalidateForLayerChange(nsDisplayItem
* aItem
,
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();
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
);
2312 InvalidatePostTransformRegion(t
,
2313 oldGeometry
->ComputeInvalidationRegion(),
2315 mLayerBuilder
->GetLastPaintOffset(t
));
2318 ThebesLayer
* newThebesLayer
= aNewLayer
->AsThebesLayer();
2319 if (newThebesLayer
) {
2320 InvalidatePostTransformRegion(newThebesLayer
,
2321 aGeometry
->ComputeInvalidationRegion(),
2323 GetTranslationForThebesLayer(newThebesLayer
));
2326 aItem
->NotifyRenderingChanged();
2333 ThebesLayer
* newThebesLayer
= aNewLayer
->AsThebesLayer();
2334 if (!newThebesLayer
) {
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.
2345 nsPoint shift
= aTopLeft
- data
->mLastActiveScrolledRootOrigin
;
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
);
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
);
2362 // Let the display item check for geometry changes and decide what needs to be
2364 oldGeometry
->MoveBy(shift
);
2365 aItem
->ComputeInvalidationRegion(mBuilder
, oldGeometry
, &combined
);
2366 oldClip
->AddOffsetAndComputeDifference(shift
, oldGeometry
->ComputeInvalidationRegion(),
2367 aClip
, aGeometry
->ComputeInvalidationRegion(),
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
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
);
2388 if (!combined
.IsEmpty()) {
2389 aItem
->NotifyRenderingChanged();
2390 InvalidatePostTransformRegion(newThebesLayer
,
2391 combined
.ScaleToOutsidePixels(data
->mXScale
, data
->mYScale
, mAppUnitsPerDevPixel
),
2392 GetTranslationForThebesLayer(newThebesLayer
));
2397 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayer
* aLayer
,
2398 nsDisplayItem
* aItem
,
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
;
2409 bool hasClip
= false;
2410 if (aLayerState
!= LAYER_NONE
) {
2411 DisplayItemData
*data
= GetDisplayItemDataForManager(aItem
, aLayer
->Manager());
2413 tempManager
= data
->mInactiveManager
;
2416 tempManager
= new BasicLayerManager();
2419 // We need to grab these before calling AddLayerDisplayItem because it will overwrite them.
2421 FrameLayerBuilder::Clip
* oldClip
= nullptr;
2422 GetOldLayerFor(aItem
, nullptr, &oldClip
);
2423 hasClip
= ComputeCombinedClip(aClip
, oldClip
,
2424 aTopLeft
- thebesData
->mLastActiveScrolledRootOrigin
,
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
);
2438 entry
->mContainerLayerFrame
= aContainerLayerFrame
;
2439 if (entry
->mContainerLayerGeneration
== 0) {
2440 entry
->mContainerLayerGeneration
= mContainerLayerGeneration
;
2442 NS_ASSERTION(aItem
->GetUnderlyingFrame(), "Must have frame");
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
2458 tempManager
->EndTransaction(nullptr, nullptr);
2459 tempManager
->SetUserData(&gLayerManagerLayerBuilder
, nullptr);
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
;
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
);
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
);
2512 if (!oldData
->mUsed
) {
2513 oldData
->UpdateContents(aLayer
, aState
, mContainerLayerGeneration
, aItem
);
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
);
2539 FrameLayerBuilder::StoreDataForFrame(nsIFrame
* aFrame
,
2540 uint32_t aDisplayItemKey
,
2544 DisplayItemData
* oldData
= GetDisplayItemData(aFrame
, aDisplayItemKey
);
2545 if (oldData
&& oldData
->mFrameList
.Length() == 1) {
2546 oldData
->UpdateContents(aLayer
, aState
, mContainerLayerGeneration
);
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);
2577 FrameLayerBuilder::AddLayerDisplayItem(Layer
* aLayer
,
2578 nsDisplayItem
* aItem
,
2580 LayerState aLayerState
,
2581 const nsPoint
& aTopLeft
,
2582 LayerManager
* aManager
,
2583 nsAutoPtr
<nsDisplayItemGeometry
> aGeometry
)
2585 if (aLayer
->Manager() != mRetainingManager
)
2588 DisplayItemData
*data
= StoreDataForFrame(aItem
, aLayer
, aLayerState
);
2589 ThebesLayer
*t
= aLayer
->AsThebesLayer();
2591 data
->mGeometry
= aGeometry
;
2592 data
->mClip
= aClip
;
2594 data
->mInactiveManager
= aManager
;
2598 FrameLayerBuilder::GetLastPaintOffset(ThebesLayer
* aLayer
)
2600 ThebesLayerItemsEntry
* entry
= mThebesLayerItems
.PutEntry(aLayer
);
2602 if (entry
->mContainerLayerGeneration
== 0) {
2603 entry
->mContainerLayerGeneration
= mContainerLayerGeneration
;
2605 if (entry
->mHasExplicitLastPaintOffset
)
2606 return entry
->mLastPaintOffset
;
2608 return GetTranslationForThebesLayer(aLayer
);
2612 FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer
* aLayer
)
2614 ThebesLayerItemsEntry
* entry
= mThebesLayerItems
.PutEntry(aLayer
);
2616 if (entry
->mContainerLayerGeneration
== 0) {
2617 entry
->mContainerLayerGeneration
= mContainerLayerGeneration
;
2619 entry
->mLastPaintOffset
= GetTranslationForThebesLayer(aLayer
);
2620 entry
->mHasExplicitLastPaintOffset
= true;
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
));
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
2667 mContainerLayer
->InsertAfter(layer
, prevChild
);
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.
2678 layer
= mContainerLayer
->GetFirstChild();
2680 layer
= layer
->GetNextSibling();
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
,
2702 gfx3DMatrix transform
=
2703 gfx3DMatrix::ScalingMatrix(aIncomingScale
.mXScale
, aIncomingScale
.mYScale
, 1.0);
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
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
);
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
2742 if (aContainerFrame
->GetContent() &&
2743 nsLayoutUtils::HasAnimationsForCompositor(
2744 aContainerFrame
->GetContent(), eCSSProperty_transform
)) {
2745 scale
= nsLayoutUtils::GetMaximumAnimatedScale(aContainerFrame
->GetContent());
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
) &&
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.
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)) {
2770 scale
.width
= gfxUtils::ClampToScaleFactor(scale
.width
);
2771 scale
.height
= gfxUtils::ClampToScaleFactor(scale
.height
);
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);
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
);
2796 result
.mInTransformedSubtree
= true;
2797 if (aContainerFrame
->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer
)) {
2798 result
.mInActiveTransformedSubtree
= true;
2801 if (isRetained
&& (!canDraw2D
|| transform2d
.HasNonIntegerTranslation())) {
2802 result
.mDisableSubpixelAntialiasingInDescendants
= true;
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
);
2868 DisplayItemData
*data
= GetOldLayerForFrame(aContainerFrame
, containerDisplayItemKey
);
2870 oldLayer
= data
->mLayer
;
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.
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
)
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
);
2924 StoreDataForFrame(aContainerFrame
, containerDisplayItemKey
, containerLayer
, LAYER_ACTIVE
);
2928 LayerManagerData
* data
= static_cast<LayerManagerData
*>
2929 (aManager
->GetUserData(&gLayerManagerUserData
));
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
;
2941 ContainerState
state(aBuilder
, aManager
, aManager
->GetLayerBuilder(),
2942 aContainerFrame
, aContainerItem
,
2943 containerLayer
, scaleParameters
);
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() &&
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
);
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
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();
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
);
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
3010 // Clear clip rect; the caller is responsible for setting it.
3011 layer
->SetClipRect(nullptr);
3012 layer
->SetMaskLayer(nullptr);
3017 FrameLayerBuilder::InvalidateAllLayers(LayerManager
* aManager
)
3019 LayerManagerData
* data
= static_cast<LayerManagerData
*>
3020 (aManager
->GetUserData(&gLayerManagerUserData
));
3022 data
->mInvalidateAllLayers
= true;
3027 FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame
*aFrame
)
3029 nsTArray
<DisplayItemData
*> *array
=
3030 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
3032 for (uint32_t i
= 0; i
< array
->Length(); i
++) {
3033 array
->ElementAt(i
)->mParent
->mInvalidateAllLayers
= true;
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()));
3049 for (uint32_t i
= 0; i
< array
->Length(); i
++) {
3050 DisplayItemData
*element
= array
->ElementAt(i
);
3051 if (!element
->mParent
->mLayerManager
->IsWidgetLayerManager()) {
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
)) {
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);
3089 FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame
* aFrame
)
3092 for (nsIFrame
* f
= aFrame
; f
; f
= nsLayoutUtils::GetCrossDocParentFrame(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.
3102 nsTArray
<DisplayItemData
*> *array
=
3103 reinterpret_cast<nsTArray
<DisplayItemData
*>*>(aFrame
->Properties().Get(LayerManagerDataProperty()));
3108 for (uint32_t i
= 0; i
< array
->Length(); i
++) {
3109 Layer
* layer
= array
->ElementAt(i
)->mLayer
;
3110 ContainerLayer
* container
= layer
->AsContainerLayer();
3112 !layer
->Manager()->IsWidgetLayerManager()) {
3115 for (Layer
* l
= container
->GetFirstChild(); l
; l
= l
->GetNextSibling()) {
3116 ThebesDisplayItemLayerUserData
* data
=
3117 static_cast<ThebesDisplayItemLayerUserData
*>
3118 (l
->GetUserData(&gThebesDisplayItemLayerUserData
));
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
)
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();
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.
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
*>
3197 FrameLayerBuilder
*layerBuilder
= aLayer
->Manager()->GetLayerBuilder();
3198 NS_ASSERTION(layerBuilder
, "Unexpectedly null layer builder!");
3200 if (layerBuilder
->CheckDOMModified())
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
) {
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
));
3233 // make the origin of the context coincide with the origin of the
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();
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
);
3274 // Do a little dance to account for the fact that we're clipping
3275 // to cdi->mClipRect
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()) {
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
);
3301 bool setClipRect
= false;
3303 for (i
= 0; i
< items
.Length(); ++i
) {
3304 ClippedDisplayItem
* cdi
= &items
[i
];
3306 if (cdi
->mItem
->GetVisibleRect().IsEmpty())
3309 // If the new desired clip state is different from the current state,
3311 if (setClipRect
!= cdi
->mClip
.mHaveClipRect
||
3312 (cdi
->mClip
.mHaveClipRect
&& cdi
->mClip
!= currentClip
)) {
3314 aContext
->Restore();
3316 setClipRect
= cdi
->mClip
.mHaveClipRect
;
3318 currentClip
= cdi
->mClip
;
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
);
3329 nsIFrame
* frame
= cdi
->mItem
->GetUnderlyingFrame();
3331 frame
->AddStateBits(NS_FRAME_PAINTED_THEBES
);
3333 #ifdef MOZ_DUMP_PAINTING
3335 if (gfxUtils::sDumpPainting
) {
3336 DebugPaintItem(rc
, cdi
->mItem
, builder
);
3341 cdi
->mItem
->Paint(builder
, rc
);
3345 if (layerBuilder
->CheckDOMModified())
3350 ThebesLayerItemsEntry
* entry
=
3351 layerBuilder
->mThebesLayerItems
.GetEntry(aLayer
);
3352 items
.SwapElements(entry
->mItems
);
3356 aContext
->Restore();
3359 FlashPaint(aContext
);
3360 if (!aRegionToInvalidate
.IsEmpty()) {
3361 aLayer
->AddInvalidRect(aRegionToInvalidate
.GetBounds());
3366 FrameLayerBuilder::CheckDOMModified()
3368 if (!mRootPresContext
||
3369 mInitialDOMGeneration
== mRootPresContext
->GetDOMGeneration())
3371 if (mDetectedDOMModification
) {
3372 // Don't spam the console with extra warnings
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!");
3383 #ifdef MOZ_DUMP_PAINTING
3385 FrameLayerBuilder::DumpRetainedLayerTree(LayerManager
* aManager
, FILE* aFile
, bool aDumpHtml
)
3387 aManager
->Dump(aFile
, "", aDumpHtml
);
3391 FrameLayerBuilder::Clip::Clip(const Clip
& aOther
, nsDisplayItem
* aClipItem
)
3392 : mRoundedClipRects(aOther
.mRoundedClipRects
),
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
3402 if (aOther
.mHaveClipRect
) {
3403 mClipRect
.IntersectRect(aOther
.mClipRect
, item
->GetClipRect());
3405 mClipRect
= item
->GetClipRect();
3408 if (type
== nsDisplayItem::TYPE_CLIP_ROUNDED_RECT
) {
3409 RoundedRect
*rr
= mRoundedClipRects
.AppendElement();
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.
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
);
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);
3439 FrameLayerBuilder::Clip::ApplyRoundedRectsTo(gfxContext
* aContext
,
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
]);
3452 FrameLayerBuilder::Clip::DrawRoundedRectsTo(gfxContext
* aContext
,
3454 uint32_t aBegin
, uint32_t aEnd
) const
3456 aEnd
= std::min
<uint32_t>(aEnd
, mRoundedClipRects
.Length());
3458 if (aEnd
- aBegin
== 0)
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]);
3469 FrameLayerBuilder::Clip::AddRoundedRectPathTo(gfxContext
* aContext
,
3471 const RoundedRect
&aRoundRect
) const
3473 gfxCornerSizes pixelRadii
;
3474 nsCSSRendering::ComputePixelRadii(aRoundRect
.mRadii
, A2D
, &pixelRadii
);
3476 gfxRect clip
= nsLayoutUtils::RectToGfxRect(aRoundRect
.mRect
, A2D
);
3480 aContext
->NewPath();
3481 aContext
->RoundedRectangle(clip
, pixelRadii
);
3485 FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect
& aRect
) const
3488 if (mHaveClipRect
) {
3489 r
.IntersectRect(r
, mClipRect
);
3491 for (uint32_t i
= 0, iEnd
= mRoundedClipRects
.Length();
3493 const Clip::RoundedRect
&rr
= mRoundedClipRects
[i
];
3494 nsRegion rgn
= nsLayoutUtils::RoundedRectIntersectRect(rr
.mRect
, rr
.mRadii
, r
);
3495 r
= rgn
.GetLargestRectangle();
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
;
3511 FrameLayerBuilder::Clip::IsRectClippedByRoundedCorner(const nsRect
& aRect
) const
3513 if (mRoundedClipRects
.IsEmpty())
3517 rect
.IntersectRect(aRect
, NonRoundedIntersection());
3518 for (uint32_t i
= 0, iEnd
= mRoundedClipRects
.Length();
3520 const Clip::RoundedRect
&rr
= mRoundedClipRects
[i
];
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
],
3527 rr
.mRadii
[NS_CORNER_TOP_LEFT_Y
],
3528 rr
.mRect
.y
+ rr
.mRadii
[NS_CORNER_TOP_LEFT_Y
],
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
],
3539 rr
.mRadii
[NS_CORNER_TOP_RIGHT_Y
],
3540 rr
.mRect
.y
+ rr
.mRadii
[NS_CORNER_TOP_RIGHT_Y
],
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
],
3551 rr
.mRadii
[NS_CORNER_BOTTOM_LEFT_Y
],
3552 rr
.mRect
.YMost() - rr
.mRadii
[NS_CORNER_BOTTOM_LEFT_Y
],
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
],
3563 rr
.mRadii
[NS_CORNER_BOTTOM_RIGHT_Y
],
3564 rr
.mRect
.YMost() - rr
.mRadii
[NS_CORNER_BOTTOM_RIGHT_Y
],
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();
3580 result
.IntersectRect(result
, mRoundedClipRects
[i
].mRect
);
3586 FrameLayerBuilder::Clip::ApplyNonRoundedIntersection(const nsRect
& aRect
) const
3588 if (!mHaveClipRect
) {
3592 nsRect result
= aRect
.Intersect(mClipRect
);
3593 for (uint32_t i
= 0, iEnd
= mRoundedClipRects
.Length();
3595 result
.Intersect(mRoundedClipRects
[i
].mRect
);
3601 FrameLayerBuilder::Clip::RemoveRoundedCorners()
3603 if (mRoundedClipRects
.IsEmpty())
3606 mClipRect
= NonRoundedIntersection();
3607 mRoundedClipRects
.Clear();
3611 AccumulateRectDifference(const nsRect
& aR1
, const nsRect
& aR2
, nsRegion
* aOut
)
3613 if (aR1
.IsEqualInterior(aR2
))
3621 FrameLayerBuilder::Clip::AddOffsetAndComputeDifference(const nsPoint
& aOffset
,
3622 const nsRect
& aBounds
,
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
);
3633 if (mHaveClipRect
) {
3634 AccumulateRectDifference((mClipRect
+ aOffset
).Intersect(aBounds
),
3635 aOther
.mClipRect
.Intersect(aOtherBounds
),
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
));
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
);
3659 SetClipCount(ThebesDisplayItemLayerUserData
* aThebesData
,
3660 uint32_t aClipCount
)
3663 aThebesData
->mMaskClipCount
= aClipCount
;
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
);
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);
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
);
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
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
);
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);
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
);
3798 } // namespace mozilla