|
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/. */ |
|
5 |
|
6 #include "mozilla/DebugOnly.h" |
|
7 |
|
8 #include "FrameLayerBuilder.h" |
|
9 |
|
10 #include "nsDisplayList.h" |
|
11 #include "nsPresContext.h" |
|
12 #include "nsLayoutUtils.h" |
|
13 #include "Layers.h" |
|
14 #include "BasicLayers.h" |
|
15 #include "gfxUtils.h" |
|
16 #include "nsRenderingContext.h" |
|
17 #include "MaskLayerImageCache.h" |
|
18 #include "nsIScrollableFrame.h" |
|
19 #include "nsPrintfCString.h" |
|
20 #include "LayerTreeInvalidation.h" |
|
21 #include "nsSVGIntegrationUtils.h" |
|
22 #include "ImageContainer.h" |
|
23 #include "ActiveLayerTracker.h" |
|
24 #include "gfx2DGlue.h" |
|
25 |
|
26 #include "GeckoProfiler.h" |
|
27 #include "mozilla/gfx/Tools.h" |
|
28 #include "mozilla/gfx/2D.h" |
|
29 #include "gfxPrefs.h" |
|
30 |
|
31 #include <algorithm> |
|
32 |
|
33 using namespace mozilla::layers; |
|
34 using namespace mozilla::gfx; |
|
35 |
|
36 namespace mozilla { |
|
37 |
|
38 class ContainerState; |
|
39 |
|
40 FrameLayerBuilder::DisplayItemData::DisplayItemData(LayerManagerData* aParent, uint32_t aKey, |
|
41 Layer* aLayer, LayerState aLayerState, uint32_t aGeneration) |
|
42 |
|
43 : mParent(aParent) |
|
44 , mLayer(aLayer) |
|
45 , mDisplayItemKey(aKey) |
|
46 , mContainerLayerGeneration(aGeneration) |
|
47 , mLayerState(aLayerState) |
|
48 , mUsed(true) |
|
49 , mIsInvalid(false) |
|
50 { |
|
51 } |
|
52 |
|
53 FrameLayerBuilder::DisplayItemData::DisplayItemData(DisplayItemData &toCopy) |
|
54 { |
|
55 // This isn't actually a copy-constructor; notice that it steals toCopy's |
|
56 // mGeometry pointer. Be careful. |
|
57 mParent = toCopy.mParent; |
|
58 mLayer = toCopy.mLayer; |
|
59 mInactiveManager = toCopy.mInactiveManager; |
|
60 mFrameList = toCopy.mFrameList; |
|
61 mGeometry = toCopy.mGeometry; |
|
62 mDisplayItemKey = toCopy.mDisplayItemKey; |
|
63 mClip = toCopy.mClip; |
|
64 mContainerLayerGeneration = toCopy.mContainerLayerGeneration; |
|
65 mLayerState = toCopy.mLayerState; |
|
66 mUsed = toCopy.mUsed; |
|
67 } |
|
68 |
|
69 void |
|
70 FrameLayerBuilder::DisplayItemData::AddFrame(nsIFrame* aFrame) |
|
71 { |
|
72 mFrameList.AppendElement(aFrame); |
|
73 |
|
74 nsTArray<DisplayItemData*> *array = |
|
75 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty())); |
|
76 if (!array) { |
|
77 array = new nsTArray<DisplayItemData*>(); |
|
78 aFrame->Properties().Set(FrameLayerBuilder::LayerManagerDataProperty(), array); |
|
79 } |
|
80 array->AppendElement(this); |
|
81 } |
|
82 |
|
83 void |
|
84 FrameLayerBuilder::DisplayItemData::RemoveFrame(nsIFrame* aFrame) |
|
85 { |
|
86 DebugOnly<bool> result = mFrameList.RemoveElement(aFrame); |
|
87 NS_ASSERTION(result, "Can't remove a frame that wasn't added!"); |
|
88 |
|
89 nsTArray<DisplayItemData*> *array = |
|
90 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(FrameLayerBuilder::LayerManagerDataProperty())); |
|
91 NS_ASSERTION(array, "Must be already stored on the frame!"); |
|
92 array->RemoveElement(this); |
|
93 } |
|
94 |
|
95 void |
|
96 FrameLayerBuilder::DisplayItemData::UpdateContents(Layer* aLayer, LayerState aState, |
|
97 uint32_t aContainerLayerGeneration, |
|
98 nsDisplayItem* aItem /* = nullptr */) |
|
99 { |
|
100 mLayer = aLayer; |
|
101 mOptLayer = nullptr; |
|
102 mInactiveManager = nullptr; |
|
103 mLayerState = aState; |
|
104 mContainerLayerGeneration = aContainerLayerGeneration; |
|
105 mGeometry = nullptr; |
|
106 mClip = DisplayItemClip(); |
|
107 mUsed = true; |
|
108 |
|
109 if (!aItem) { |
|
110 return; |
|
111 } |
|
112 |
|
113 nsAutoTArray<nsIFrame*, 4> copy(mFrameList); |
|
114 if (!copy.RemoveElement(aItem->Frame())) { |
|
115 AddFrame(aItem->Frame()); |
|
116 } |
|
117 |
|
118 nsAutoTArray<nsIFrame*,4> mergedFrames; |
|
119 aItem->GetMergedFrames(&mergedFrames); |
|
120 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { |
|
121 if (!copy.RemoveElement(mergedFrames[i])) { |
|
122 AddFrame(mergedFrames[i]); |
|
123 } |
|
124 } |
|
125 |
|
126 for (uint32_t i = 0; i < copy.Length(); i++) { |
|
127 RemoveFrame(copy[i]); |
|
128 } |
|
129 } |
|
130 |
|
131 static nsIFrame* sDestroyedFrame = nullptr; |
|
132 FrameLayerBuilder::DisplayItemData::~DisplayItemData() |
|
133 { |
|
134 for (uint32_t i = 0; i < mFrameList.Length(); i++) { |
|
135 nsIFrame* frame = mFrameList[i]; |
|
136 if (frame == sDestroyedFrame) { |
|
137 continue; |
|
138 } |
|
139 nsTArray<DisplayItemData*> *array = |
|
140 reinterpret_cast<nsTArray<DisplayItemData*>*>(frame->Properties().Get(LayerManagerDataProperty())); |
|
141 array->RemoveElement(this); |
|
142 } |
|
143 } |
|
144 |
|
145 void |
|
146 FrameLayerBuilder::DisplayItemData::GetFrameListChanges(nsDisplayItem* aOther, |
|
147 nsTArray<nsIFrame*>& aOut) |
|
148 { |
|
149 aOut = mFrameList; |
|
150 nsAutoTArray<nsIFrame*, 4> added; |
|
151 if (!aOut.RemoveElement(aOther->Frame())) { |
|
152 added.AppendElement(aOther->Frame()); |
|
153 } |
|
154 |
|
155 nsAutoTArray<nsIFrame*,4> mergedFrames; |
|
156 aOther->GetMergedFrames(&mergedFrames); |
|
157 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { |
|
158 if (!aOut.RemoveElement(mergedFrames[i])) { |
|
159 added.AppendElement(mergedFrames[i]); |
|
160 } |
|
161 } |
|
162 |
|
163 aOut.AppendElements(added); |
|
164 } |
|
165 |
|
166 /** |
|
167 * This is the userdata we associate with a layer manager. |
|
168 */ |
|
169 class LayerManagerData : public LayerUserData { |
|
170 public: |
|
171 LayerManagerData(LayerManager *aManager) |
|
172 : mLayerManager(aManager) |
|
173 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
174 , mParent(nullptr) |
|
175 #endif |
|
176 , mInvalidateAllLayers(false) |
|
177 { |
|
178 MOZ_COUNT_CTOR(LayerManagerData); |
|
179 } |
|
180 ~LayerManagerData() { |
|
181 MOZ_COUNT_DTOR(LayerManagerData); |
|
182 } |
|
183 |
|
184 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
185 void Dump(const char *aPrefix = "") { |
|
186 printf_stderr("%sLayerManagerData %p\n", aPrefix, this); |
|
187 nsAutoCString prefix; |
|
188 prefix += aPrefix; |
|
189 prefix += " "; |
|
190 mDisplayItems.EnumerateEntries( |
|
191 FrameLayerBuilder::DumpDisplayItemDataForFrame, (void*)prefix.get()); |
|
192 } |
|
193 #endif |
|
194 |
|
195 /** |
|
196 * Tracks which frames have layers associated with them. |
|
197 */ |
|
198 LayerManager *mLayerManager; |
|
199 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
200 LayerManagerData *mParent; |
|
201 #endif |
|
202 nsTHashtable<nsRefPtrHashKey<FrameLayerBuilder::DisplayItemData> > mDisplayItems; |
|
203 bool mInvalidateAllLayers; |
|
204 }; |
|
205 |
|
206 /* static */ void |
|
207 FrameLayerBuilder::DestroyDisplayItemDataFor(nsIFrame* aFrame) |
|
208 { |
|
209 FrameProperties props = aFrame->Properties(); |
|
210 props.Delete(LayerManagerDataProperty()); |
|
211 } |
|
212 |
|
213 // a global cache of image containers used for mask layers |
|
214 static MaskLayerImageCache* gMaskLayerImageCache = nullptr; |
|
215 |
|
216 static inline MaskLayerImageCache* GetMaskLayerImageCache() |
|
217 { |
|
218 if (!gMaskLayerImageCache) { |
|
219 gMaskLayerImageCache = new MaskLayerImageCache(); |
|
220 } |
|
221 |
|
222 return gMaskLayerImageCache; |
|
223 } |
|
224 |
|
225 /** |
|
226 * We keep a stack of these to represent the ThebesLayers that are |
|
227 * currently available to have display items added to. |
|
228 * We use a stack here because as much as possible we want to |
|
229 * assign display items to existing ThebesLayers, and to the lowest |
|
230 * ThebesLayer in z-order. This reduces the number of layers and |
|
231 * makes it more likely a display item will be rendered to an opaque |
|
232 * layer, giving us the best chance of getting subpixel AA. |
|
233 */ |
|
234 class ThebesLayerData { |
|
235 public: |
|
236 ThebesLayerData() : |
|
237 mAnimatedGeometryRoot(nullptr), |
|
238 mFixedPosFrameForLayerData(nullptr), |
|
239 mReferenceFrame(nullptr), |
|
240 mLayer(nullptr), |
|
241 mIsSolidColorInVisibleRegion(false), |
|
242 mSingleItemFixedToViewport(false), |
|
243 mNeedComponentAlpha(false), |
|
244 mForceTransparentSurface(false), |
|
245 mImage(nullptr), |
|
246 mCommonClipCount(-1), |
|
247 mAllDrawingAbove(false) |
|
248 {} |
|
249 /** |
|
250 * Record that an item has been added to the ThebesLayer, so we |
|
251 * need to update our regions. |
|
252 * @param aVisibleRect the area of the item that's visible |
|
253 * @param aDrawRect the area of the item that would be drawn if it |
|
254 * was completely visible |
|
255 * @param aOpaqueRect if non-null, the area of the item that's opaque. |
|
256 * We pass in a separate opaque rect because the opaque rect can be |
|
257 * bigger than the visible rect, and we want to have the biggest |
|
258 * opaque rect that we can. |
|
259 * @param aSolidColor if non-null, the visible area of the item is |
|
260 * a constant color given by *aSolidColor |
|
261 */ |
|
262 void Accumulate(ContainerState* aState, |
|
263 nsDisplayItem* aItem, |
|
264 const nsIntRect& aVisibleRect, |
|
265 const nsIntRect& aDrawRect, |
|
266 const DisplayItemClip& aClip); |
|
267 const nsIFrame* GetAnimatedGeometryRoot() { return mAnimatedGeometryRoot; } |
|
268 |
|
269 /** |
|
270 * Add aHitRegion and aDispatchToContentHitRegion to the hit regions for |
|
271 * this ThebesLayer. |
|
272 */ |
|
273 void AccumulateEventRegions(const nsIntRegion& aHitRegion, |
|
274 const nsIntRegion& aMaybeHitRegion, |
|
275 const nsIntRegion& aDispatchToContentHitRegion) |
|
276 { |
|
277 mHitRegion.Or(mHitRegion, aHitRegion); |
|
278 mMaybeHitRegion.Or(mMaybeHitRegion, aMaybeHitRegion); |
|
279 mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aDispatchToContentHitRegion); |
|
280 } |
|
281 |
|
282 /** |
|
283 * If this represents only a nsDisplayImage, and the image type |
|
284 * supports being optimized to an ImageLayer (TYPE_RASTER only) returns |
|
285 * an ImageContainer for the image. |
|
286 */ |
|
287 already_AddRefed<ImageContainer> CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder); |
|
288 |
|
289 void AddDrawAboveRegion(const nsIntRegion& aAbove) |
|
290 { |
|
291 if (!mAllDrawingAbove) { |
|
292 mDrawAboveRegion.Or(mDrawAboveRegion, aAbove); |
|
293 mDrawAboveRegion.SimplifyOutward(4); |
|
294 } |
|
295 } |
|
296 |
|
297 void AddVisibleAboveRegion(const nsIntRegion& aAbove) |
|
298 { |
|
299 if (!mAllDrawingAbove) { |
|
300 mVisibleAboveRegion.Or(mVisibleAboveRegion, aAbove); |
|
301 mVisibleAboveRegion.SimplifyOutward(4); |
|
302 } |
|
303 } |
|
304 |
|
305 void CopyAboveRegion(ThebesLayerData* aOther) |
|
306 { |
|
307 if (aOther->mAllDrawingAbove || mAllDrawingAbove) { |
|
308 SetAllDrawingAbove(); |
|
309 } else { |
|
310 mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleAboveRegion); |
|
311 mVisibleAboveRegion.Or(mVisibleAboveRegion, aOther->mVisibleRegion); |
|
312 mVisibleAboveRegion.SimplifyOutward(4); |
|
313 mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawAboveRegion); |
|
314 mDrawAboveRegion.Or(mDrawAboveRegion, aOther->mDrawRegion); |
|
315 mDrawAboveRegion.SimplifyOutward(4); |
|
316 } |
|
317 } |
|
318 |
|
319 void SetAllDrawingAbove() |
|
320 { |
|
321 mAllDrawingAbove = true; |
|
322 mDrawAboveRegion.SetEmpty(); |
|
323 mVisibleAboveRegion.SetEmpty(); |
|
324 } |
|
325 |
|
326 bool DrawAboveRegionIntersects(const nsIntRect& aRect) |
|
327 { |
|
328 return mAllDrawingAbove || mDrawAboveRegion.Intersects(aRect); |
|
329 } |
|
330 |
|
331 bool DrawRegionIntersects(const nsIntRect& aRect) |
|
332 { |
|
333 return IsSubjectToAsyncTransforms() || mDrawRegion.Intersects(aRect); |
|
334 } |
|
335 |
|
336 bool IntersectsVisibleAboveRegion(const nsIntRegion& aVisibleRegion) |
|
337 { |
|
338 if (mAllDrawingAbove) { |
|
339 return true; |
|
340 } |
|
341 nsIntRegion visibleAboveIntersection; |
|
342 visibleAboveIntersection.And(mVisibleAboveRegion, aVisibleRegion); |
|
343 if (visibleAboveIntersection.IsEmpty()) { |
|
344 return false; |
|
345 } |
|
346 return true; |
|
347 } |
|
348 |
|
349 bool IsSubjectToAsyncTransforms() |
|
350 { |
|
351 return mFixedPosFrameForLayerData != nullptr; |
|
352 } |
|
353 |
|
354 /** |
|
355 * The region of visible content in the layer, relative to the |
|
356 * container layer (which is at the snapped top-left of the display |
|
357 * list reference frame). |
|
358 */ |
|
359 nsIntRegion mVisibleRegion; |
|
360 /** |
|
361 * The region containing the bounds of all display items in the layer, |
|
362 * regardless of visbility. |
|
363 * Same coordinate system as mVisibleRegion. |
|
364 * This is a conservative approximation: it contains the true region. |
|
365 */ |
|
366 nsIntRegion mDrawRegion; |
|
367 /** |
|
368 * The region of visible content in the layer that is opaque. |
|
369 * Same coordinate system as mVisibleRegion. |
|
370 */ |
|
371 nsIntRegion mOpaqueRegion; |
|
372 /** |
|
373 * The definitely-hit region for this ThebesLayer. |
|
374 */ |
|
375 nsIntRegion mHitRegion; |
|
376 /** |
|
377 * The maybe-hit region for this ThebesLayer. |
|
378 */ |
|
379 nsIntRegion mMaybeHitRegion; |
|
380 /** |
|
381 * The dispatch-to-content hit region for this ThebesLayer. |
|
382 */ |
|
383 nsIntRegion mDispatchToContentHitRegion; |
|
384 /** |
|
385 * The "active scrolled root" for all content in the layer. Must |
|
386 * be non-null; all content in a ThebesLayer must have the same |
|
387 * active scrolled root. |
|
388 */ |
|
389 const nsIFrame* mAnimatedGeometryRoot; |
|
390 /** |
|
391 * If non-null, the frame from which we'll extract "fixed positioning" |
|
392 * metadata for this layer. This can be a position:fixed frame or a viewport |
|
393 * frame; the latter case is used for background-attachment:fixed content. |
|
394 */ |
|
395 const nsIFrame* mFixedPosFrameForLayerData; |
|
396 const nsIFrame* mReferenceFrame; |
|
397 ThebesLayer* mLayer; |
|
398 /** |
|
399 * If mIsSolidColorInVisibleRegion is true, this is the color of the visible |
|
400 * region. |
|
401 */ |
|
402 nscolor mSolidColor; |
|
403 /** |
|
404 * True if every pixel in mVisibleRegion will have color mSolidColor. |
|
405 */ |
|
406 bool mIsSolidColorInVisibleRegion; |
|
407 /** |
|
408 * True if the layer contains exactly one item that returned true for |
|
409 * ShouldFixToViewport. |
|
410 */ |
|
411 bool mSingleItemFixedToViewport; |
|
412 /** |
|
413 * True if there is any text visible in the layer that's over |
|
414 * transparent pixels in the layer. |
|
415 */ |
|
416 bool mNeedComponentAlpha; |
|
417 /** |
|
418 * Set if the layer should be treated as transparent, even if its entire |
|
419 * area is covered by opaque display items. For example, this needs to |
|
420 * be set if something is going to "punch holes" in the layer by clearing |
|
421 * part of its surface. |
|
422 */ |
|
423 bool mForceTransparentSurface; |
|
424 |
|
425 /** |
|
426 * Stores the pointer to the nsDisplayImage if we want to |
|
427 * convert this to an ImageLayer. |
|
428 */ |
|
429 nsDisplayImageContainer* mImage; |
|
430 /** |
|
431 * Stores the clip that we need to apply to the image or, if there is no |
|
432 * image, a clip for SOME item in the layer. There is no guarantee which |
|
433 * item's clip will be stored here and mItemClip should not be used to clip |
|
434 * the whole layer - only some part of the clip should be used, as determined |
|
435 * by ThebesDisplayItemLayerUserData::GetCommonClipCount() - which may even be |
|
436 * no part at all. |
|
437 */ |
|
438 DisplayItemClip mItemClip; |
|
439 /** |
|
440 * The first mCommonClipCount rounded rectangle clips are identical for |
|
441 * all items in the layer. |
|
442 * -1 if there are no items in the layer; must be >=0 by the time that this |
|
443 * data is popped from the stack. |
|
444 */ |
|
445 int32_t mCommonClipCount; |
|
446 /* |
|
447 * Updates mCommonClipCount by checking for rounded rect clips in common |
|
448 * between the clip on a new item (aCurrentClip) and the common clips |
|
449 * on items already in the layer (the first mCommonClipCount rounded rects |
|
450 * in mItemClip). |
|
451 */ |
|
452 void UpdateCommonClipCount(const DisplayItemClip& aCurrentClip); |
|
453 |
|
454 private: |
|
455 /** |
|
456 * The region of visible content above the layer and below the |
|
457 * next ThebesLayerData currently in the stack, if any. Note that not |
|
458 * all ThebesLayers for the container are in the ThebesLayerData stack. |
|
459 * Same coordinate system as mVisibleRegion. |
|
460 * This is a conservative approximation: it contains the true region. |
|
461 */ |
|
462 nsIntRegion mVisibleAboveRegion; |
|
463 /** |
|
464 * The region containing the bounds of all display items (regardless |
|
465 * of visibility) in the layer and below the next ThebesLayerData |
|
466 * currently in the stack, if any. |
|
467 * Note that not all ThebesLayers for the container are in the |
|
468 * ThebesLayerData stack. |
|
469 * Same coordinate system as mVisibleRegion. |
|
470 */ |
|
471 nsIntRegion mDrawAboveRegion; |
|
472 /** |
|
473 * True if mDrawAboveRegion and mVisibleAboveRegion should be treated |
|
474 * as infinite, and all display items should be considered 'above' this layer. |
|
475 */ |
|
476 bool mAllDrawingAbove; |
|
477 }; |
|
478 |
|
479 /** |
|
480 * This is a helper object used to build up the layer children for |
|
481 * a ContainerLayer. |
|
482 */ |
|
483 class ContainerState { |
|
484 public: |
|
485 ContainerState(nsDisplayListBuilder* aBuilder, |
|
486 LayerManager* aManager, |
|
487 FrameLayerBuilder* aLayerBuilder, |
|
488 nsIFrame* aContainerFrame, |
|
489 nsDisplayItem* aContainerItem, |
|
490 ContainerLayer* aContainerLayer, |
|
491 const ContainerLayerParameters& aParameters) : |
|
492 mBuilder(aBuilder), mManager(aManager), |
|
493 mLayerBuilder(aLayerBuilder), |
|
494 mContainerFrame(aContainerFrame), |
|
495 mContainerLayer(aContainerLayer), |
|
496 mParameters(aParameters), |
|
497 mNextFreeRecycledThebesLayer(0) |
|
498 { |
|
499 nsPresContext* presContext = aContainerFrame->PresContext(); |
|
500 mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
501 mContainerReferenceFrame = aContainerItem ? aContainerItem->ReferenceFrameForChildren() : |
|
502 mBuilder->FindReferenceFrameFor(mContainerFrame); |
|
503 mContainerAnimatedGeometryRoot = aContainerItem |
|
504 ? nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder) |
|
505 : mContainerReferenceFrame; |
|
506 // When AllowResidualTranslation is false, display items will be drawn |
|
507 // scaled with a translation by integer pixels, so we know how the snapping |
|
508 // will work. |
|
509 mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() && |
|
510 !mParameters.AllowResidualTranslation(); |
|
511 CollectOldLayers(); |
|
512 } |
|
513 |
|
514 enum ProcessDisplayItemsFlags { |
|
515 NO_COMPONENT_ALPHA = 0x01, |
|
516 }; |
|
517 |
|
518 /** |
|
519 * This is the method that actually walks a display list and builds |
|
520 * the child layers. |
|
521 */ |
|
522 void ProcessDisplayItems(const nsDisplayList& aList, uint32_t aFlags); |
|
523 /** |
|
524 * This finalizes all the open ThebesLayers by popping every element off |
|
525 * mThebesLayerDataStack, then sets the children of the container layer |
|
526 * to be all the layers in mNewChildLayers in that order and removes any |
|
527 * layers as children of the container that aren't in mNewChildLayers. |
|
528 * @param aTextContentFlags if any child layer has CONTENT_COMPONENT_ALPHA, |
|
529 * set *aTextContentFlags to CONTENT_COMPONENT_ALPHA |
|
530 */ |
|
531 void Finish(uint32_t *aTextContentFlags, LayerManagerData* aData); |
|
532 |
|
533 nsRect GetChildrenBounds() { return mBounds; } |
|
534 |
|
535 nscoord GetAppUnitsPerDevPixel() { return mAppUnitsPerDevPixel; } |
|
536 |
|
537 nsIntRect ScaleToNearestPixels(const nsRect& aRect) |
|
538 { |
|
539 return aRect.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale, |
|
540 mAppUnitsPerDevPixel); |
|
541 } |
|
542 nsIntRegion ScaleRegionToNearestPixels(const nsRegion& aRegion) |
|
543 { |
|
544 return aRegion.ScaleToNearestPixels(mParameters.mXScale, mParameters.mYScale, |
|
545 mAppUnitsPerDevPixel); |
|
546 } |
|
547 nsIntRect ScaleToOutsidePixels(const nsRect& aRect, bool aSnap = false) |
|
548 { |
|
549 if (aSnap && mSnappingEnabled) { |
|
550 return ScaleToNearestPixels(aRect); |
|
551 } |
|
552 return aRect.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale, |
|
553 mAppUnitsPerDevPixel); |
|
554 } |
|
555 nsIntRect ScaleToInsidePixels(const nsRect& aRect, bool aSnap = false) |
|
556 { |
|
557 if (aSnap && mSnappingEnabled) { |
|
558 return ScaleToNearestPixels(aRect); |
|
559 } |
|
560 return aRect.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale, |
|
561 mAppUnitsPerDevPixel); |
|
562 } |
|
563 |
|
564 nsIntRegion ScaleRegionToInsidePixels(const nsRegion& aRegion, bool aSnap = false) |
|
565 { |
|
566 if (aSnap && mSnappingEnabled) { |
|
567 return ScaleRegionToNearestPixels(aRegion); |
|
568 } |
|
569 return aRegion.ScaleToInsidePixels(mParameters.mXScale, mParameters.mYScale, |
|
570 mAppUnitsPerDevPixel); |
|
571 } |
|
572 |
|
573 nsIntRegion ScaleRegionToOutsidePixels(const nsRegion& aRegion, bool aSnap = false) |
|
574 { |
|
575 if (aSnap && mSnappingEnabled) { |
|
576 return ScaleRegionToNearestPixels(aRegion); |
|
577 } |
|
578 return aRegion.ScaleToOutsidePixels(mParameters.mXScale, mParameters.mYScale, |
|
579 mAppUnitsPerDevPixel); |
|
580 } |
|
581 |
|
582 protected: |
|
583 friend class ThebesLayerData; |
|
584 |
|
585 /** |
|
586 * Grab the next recyclable ThebesLayer, or create one if there are no |
|
587 * more recyclable ThebesLayers. Does any necessary invalidation of |
|
588 * a recycled ThebesLayer, and sets up the transform on the ThebesLayer |
|
589 * to account for scrolling. |
|
590 */ |
|
591 already_AddRefed<ThebesLayer> CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot, |
|
592 const nsIFrame *aReferenceFrame, |
|
593 const nsPoint& aTopLeft); |
|
594 /** |
|
595 * Grab the next recyclable ColorLayer, or create one if there are no |
|
596 * more recyclable ColorLayers. |
|
597 */ |
|
598 already_AddRefed<ColorLayer> CreateOrRecycleColorLayer(ThebesLayer* aThebes); |
|
599 /** |
|
600 * Grab the next recyclable ImageLayer, or create one if there are no |
|
601 * more recyclable ImageLayers. |
|
602 */ |
|
603 already_AddRefed<ImageLayer> CreateOrRecycleImageLayer(ThebesLayer* aThebes); |
|
604 /** |
|
605 * Grab a recyclable ImageLayer for use as a mask layer for aLayer (that is a |
|
606 * mask layer which has been used for aLayer before), or create one if such |
|
607 * a layer doesn't exist. |
|
608 */ |
|
609 already_AddRefed<ImageLayer> CreateOrRecycleMaskImageLayerFor(Layer* aLayer); |
|
610 /** |
|
611 * Grabs all ThebesLayers and ColorLayers from the ContainerLayer and makes them |
|
612 * available for recycling. |
|
613 */ |
|
614 void CollectOldLayers(); |
|
615 /** |
|
616 * If aItem used to belong to a ThebesLayer, invalidates the area of |
|
617 * aItem in that layer. If aNewLayer is a ThebesLayer, invalidates the area of |
|
618 * aItem in that layer. |
|
619 */ |
|
620 void InvalidateForLayerChange(nsDisplayItem* aItem, |
|
621 Layer* aNewLayer, |
|
622 const DisplayItemClip& aClip, |
|
623 const nsPoint& aTopLeft, |
|
624 nsDisplayItemGeometry *aGeometry); |
|
625 /** |
|
626 * Try to determine whether the ThebesLayer at aThebesLayerIndex |
|
627 * has a single opaque color behind it, over the entire bounds of its visible |
|
628 * region. |
|
629 * If successful, return that color, otherwise return NS_RGBA(0,0,0,0). |
|
630 */ |
|
631 nscolor FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex); |
|
632 /** |
|
633 * Find the fixed-pos frame, if any, containing (or equal to) |
|
634 * aAnimatedGeometryRoot. Only return a fixed-pos frame if its viewport |
|
635 * has a displayport. Updates *aVisibleRegion to be the intersection of |
|
636 * aDrawRegion and the displayport, and updates *aIsSolidColorInVisibleRegion |
|
637 * (if non-null) to false if the visible region grows. |
|
638 * aDisplayItemFixedToViewport is true if the layer contains a single display |
|
639 * item which returned true for ShouldFixToViewport. |
|
640 * This can return the actual viewport frame for layers whose display items |
|
641 * are directly on the viewport (e.g. background-attachment:fixed backgrounds). |
|
642 */ |
|
643 const nsIFrame* FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot, |
|
644 bool aDisplayItemFixedToViewport); |
|
645 void AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame, |
|
646 const nsIntRegion& aDrawRegion, |
|
647 nsIntRegion* aVisibleRegion, |
|
648 bool* aIsSolidColorInVisibleRegion = nullptr); |
|
649 /** |
|
650 * Set fixed-pos layer metadata on aLayer according to the data for aFixedPosFrame. |
|
651 */ |
|
652 void SetFixedPositionLayerData(Layer* aLayer, |
|
653 const nsIFrame* aFixedPosFrame); |
|
654 /** |
|
655 * Indicate that we are done adding items to the ThebesLayer at the top of |
|
656 * mThebesLayerDataStack. Set the final visible region and opaque-content |
|
657 * flag, and pop it off the stack. |
|
658 */ |
|
659 void PopThebesLayerData(); |
|
660 /** |
|
661 * Find the ThebesLayer to which we should assign the next display item. |
|
662 * We scan the ThebesLayerData stack to find the topmost ThebesLayer |
|
663 * that is compatible with the display item (i.e., has the same |
|
664 * active scrolled root), and that has no content from other layers above |
|
665 * it and intersecting the aVisibleRect. |
|
666 * Returns the layer, and also updates the ThebesLayerData. Will |
|
667 * push a new ThebesLayerData onto the stack if no suitable existing |
|
668 * layer is found. If we choose a ThebesLayer that's already on the |
|
669 * ThebesLayerData stack, later elements on the stack will be popped off. |
|
670 * @param aVisibleRect the area of the next display item that's visible |
|
671 * @param aAnimatedGeometryRoot the active scrolled root for the next |
|
672 * display item |
|
673 * @param aOpaqueRect if non-null, a region of the display item that is opaque |
|
674 * @param aSolidColor if non-null, indicates that every pixel in aVisibleRect |
|
675 * will be painted with aSolidColor by the item |
|
676 * @param aShouldFixToViewport if true, aAnimatedGeometryRoot is the viewport |
|
677 * and we will be adding fixed-pos metadata for this layer because the |
|
678 * display item returned true from ShouldFixToViewport. |
|
679 */ |
|
680 ThebesLayerData* FindThebesLayerFor(nsDisplayItem* aItem, |
|
681 const nsIntRect& aVisibleRect, |
|
682 const nsIFrame* aAnimatedGeometryRoot, |
|
683 const nsPoint& aTopLeft, |
|
684 bool aShouldFixToViewport); |
|
685 ThebesLayerData* GetTopThebesLayerData() |
|
686 { |
|
687 return mThebesLayerDataStack.IsEmpty() ? nullptr |
|
688 : mThebesLayerDataStack[mThebesLayerDataStack.Length() - 1].get(); |
|
689 } |
|
690 |
|
691 /* Build a mask layer to represent the clipping region. Will return null if |
|
692 * there is no clipping specified or a mask layer cannot be built. |
|
693 * Builds an ImageLayer for the appropriate backend; the mask is relative to |
|
694 * aLayer's visible region. |
|
695 * aLayer is the layer to be clipped. |
|
696 * aRoundedRectClipCount is used when building mask layers for ThebesLayers, |
|
697 * SetupMaskLayer will build a mask layer for only the first |
|
698 * aRoundedRectClipCount rounded rects in aClip |
|
699 */ |
|
700 void SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip, |
|
701 uint32_t aRoundedRectClipCount = UINT32_MAX); |
|
702 |
|
703 bool ChooseAnimatedGeometryRoot(const nsDisplayList& aList, |
|
704 const nsIFrame **aAnimatedGeometryRoot); |
|
705 |
|
706 nsDisplayListBuilder* mBuilder; |
|
707 LayerManager* mManager; |
|
708 FrameLayerBuilder* mLayerBuilder; |
|
709 nsIFrame* mContainerFrame; |
|
710 const nsIFrame* mContainerReferenceFrame; |
|
711 const nsIFrame* mContainerAnimatedGeometryRoot; |
|
712 ContainerLayer* mContainerLayer; |
|
713 ContainerLayerParameters mParameters; |
|
714 /** |
|
715 * The region of ThebesLayers that should be invalidated every time |
|
716 * we recycle one. |
|
717 */ |
|
718 nsIntRegion mInvalidThebesContent; |
|
719 nsRect mBounds; |
|
720 nsAutoTArray<nsAutoPtr<ThebesLayerData>,1> mThebesLayerDataStack; |
|
721 /** |
|
722 * We collect the list of children in here. During ProcessDisplayItems, |
|
723 * the layers in this array either have mContainerLayer as their parent, |
|
724 * or no parent. |
|
725 */ |
|
726 typedef nsAutoTArray<nsRefPtr<Layer>,1> AutoLayersArray; |
|
727 AutoLayersArray mNewChildLayers; |
|
728 nsTArray<nsRefPtr<ThebesLayer> > mRecycledThebesLayers; |
|
729 nsDataHashtable<nsPtrHashKey<Layer>, nsRefPtr<ImageLayer> > |
|
730 mRecycledMaskImageLayers; |
|
731 uint32_t mNextFreeRecycledThebesLayer; |
|
732 nscoord mAppUnitsPerDevPixel; |
|
733 bool mSnappingEnabled; |
|
734 }; |
|
735 |
|
736 class ThebesDisplayItemLayerUserData : public LayerUserData |
|
737 { |
|
738 public: |
|
739 ThebesDisplayItemLayerUserData() : |
|
740 mMaskClipCount(0), |
|
741 mForcedBackgroundColor(NS_RGBA(0,0,0,0)), |
|
742 mXScale(1.f), mYScale(1.f), |
|
743 mAppUnitsPerDevPixel(0), |
|
744 mTranslation(0, 0), |
|
745 mAnimatedGeometryRootPosition(0, 0) {} |
|
746 |
|
747 /** |
|
748 * Record the number of clips in the Thebes layer's mask layer. |
|
749 * Should not be reset when the layer is recycled since it is used to track |
|
750 * changes in the use of mask layers. |
|
751 */ |
|
752 uint32_t mMaskClipCount; |
|
753 |
|
754 /** |
|
755 * A color that should be painted over the bounds of the layer's visible |
|
756 * region before any other content is painted. |
|
757 */ |
|
758 nscolor mForcedBackgroundColor; |
|
759 |
|
760 /** |
|
761 * The resolution scale used. |
|
762 */ |
|
763 float mXScale, mYScale; |
|
764 |
|
765 /** |
|
766 * The appunits per dev pixel for the items in this layer. |
|
767 */ |
|
768 nscoord mAppUnitsPerDevPixel; |
|
769 |
|
770 /** |
|
771 * The offset from the ThebesLayer's 0,0 to the |
|
772 * reference frame. This isn't necessarily the same as the transform |
|
773 * set on the ThebesLayer since we might also be applying an extra |
|
774 * offset specified by the parent ContainerLayer/ |
|
775 */ |
|
776 nsIntPoint mTranslation; |
|
777 |
|
778 /** |
|
779 * We try to make 0,0 of the ThebesLayer be the top-left of the |
|
780 * border-box of the "active scrolled root" frame (i.e. the nearest ancestor |
|
781 * frame for the display items that is being actively scrolled). But |
|
782 * we force the ThebesLayer transform to be an integer translation, and we may |
|
783 * have a resolution scale, so we have to snap the ThebesLayer transform, so |
|
784 * 0,0 may not be exactly the top-left of the active scrolled root. Here we |
|
785 * store the coordinates in ThebesLayer space of the top-left of the |
|
786 * active scrolled root. |
|
787 */ |
|
788 gfxPoint mAnimatedGeometryRootPosition; |
|
789 |
|
790 nsIntRegion mRegionToInvalidate; |
|
791 |
|
792 // The offset between the active scrolled root of this layer |
|
793 // and the root of the container for the previous and current |
|
794 // paints respectively. |
|
795 nsPoint mLastAnimatedGeometryRootOrigin; |
|
796 nsPoint mAnimatedGeometryRootOrigin; |
|
797 |
|
798 nsRefPtr<ColorLayer> mColorLayer; |
|
799 nsRefPtr<ImageLayer> mImageLayer; |
|
800 }; |
|
801 |
|
802 /* |
|
803 * User data for layers which will be used as masks. |
|
804 */ |
|
805 struct MaskLayerUserData : public LayerUserData |
|
806 { |
|
807 MaskLayerUserData() |
|
808 : mScaleX(-1.0f) |
|
809 , mScaleY(-1.0f) |
|
810 , mAppUnitsPerDevPixel(-1) |
|
811 { } |
|
812 |
|
813 bool |
|
814 operator== (const MaskLayerUserData& aOther) const |
|
815 { |
|
816 return mRoundedClipRects == aOther.mRoundedClipRects && |
|
817 mScaleX == aOther.mScaleX && |
|
818 mScaleY == aOther.mScaleY && |
|
819 mOffset == aOther.mOffset && |
|
820 mAppUnitsPerDevPixel == aOther.mAppUnitsPerDevPixel; |
|
821 } |
|
822 |
|
823 nsRefPtr<const MaskLayerImageCache::MaskLayerImageKey> mImageKey; |
|
824 // properties of the mask layer; the mask layer may be re-used if these |
|
825 // remain unchanged. |
|
826 nsTArray<DisplayItemClip::RoundedRect> mRoundedClipRects; |
|
827 // scale from the masked layer which is applied to the mask |
|
828 float mScaleX, mScaleY; |
|
829 // The ContainerLayerParameters offset which is applied to the mask's transform. |
|
830 nsIntPoint mOffset; |
|
831 int32_t mAppUnitsPerDevPixel; |
|
832 }; |
|
833 |
|
834 /** |
|
835 * The address of gThebesDisplayItemLayerUserData is used as the user |
|
836 * data key for ThebesLayers created by FrameLayerBuilder. |
|
837 * It identifies ThebesLayers used to draw non-layer content, which are |
|
838 * therefore eligible for recycling. We want display items to be able to |
|
839 * create their own dedicated ThebesLayers in BuildLayer, if necessary, |
|
840 * and we wouldn't want to accidentally recycle those. |
|
841 * The user data is a ThebesDisplayItemLayerUserData. |
|
842 */ |
|
843 uint8_t gThebesDisplayItemLayerUserData; |
|
844 /** |
|
845 * The address of gColorLayerUserData is used as the user |
|
846 * data key for ColorLayers created by FrameLayerBuilder. |
|
847 * The user data is null. |
|
848 */ |
|
849 uint8_t gColorLayerUserData; |
|
850 /** |
|
851 * The address of gImageLayerUserData is used as the user |
|
852 * data key for ImageLayers created by FrameLayerBuilder. |
|
853 * The user data is null. |
|
854 */ |
|
855 uint8_t gImageLayerUserData; |
|
856 /** |
|
857 * The address of gLayerManagerUserData is used as the user |
|
858 * data key for retained LayerManagers managed by FrameLayerBuilder. |
|
859 * The user data is a LayerManagerData. |
|
860 */ |
|
861 uint8_t gLayerManagerUserData; |
|
862 /** |
|
863 * The address of gMaskLayerUserData is used as the user |
|
864 * data key for mask layers managed by FrameLayerBuilder. |
|
865 * The user data is a MaskLayerUserData. |
|
866 */ |
|
867 uint8_t gMaskLayerUserData; |
|
868 |
|
869 /** |
|
870 * Helper functions for getting user data and casting it to the correct type. |
|
871 * aLayer is the layer where the user data is stored. |
|
872 */ |
|
873 MaskLayerUserData* GetMaskLayerUserData(Layer* aLayer) |
|
874 { |
|
875 return static_cast<MaskLayerUserData*>(aLayer->GetUserData(&gMaskLayerUserData)); |
|
876 } |
|
877 |
|
878 ThebesDisplayItemLayerUserData* GetThebesDisplayItemLayerUserData(Layer* aLayer) |
|
879 { |
|
880 return static_cast<ThebesDisplayItemLayerUserData*>( |
|
881 aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
882 } |
|
883 |
|
884 /* static */ void |
|
885 FrameLayerBuilder::Shutdown() |
|
886 { |
|
887 if (gMaskLayerImageCache) { |
|
888 delete gMaskLayerImageCache; |
|
889 gMaskLayerImageCache = nullptr; |
|
890 } |
|
891 } |
|
892 |
|
893 void |
|
894 FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager, |
|
895 ThebesLayerData* aLayerData) |
|
896 { |
|
897 mDisplayListBuilder = aBuilder; |
|
898 mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext(); |
|
899 if (mRootPresContext) { |
|
900 mInitialDOMGeneration = mRootPresContext->GetDOMGeneration(); |
|
901 } |
|
902 mContainingThebesLayer = aLayerData; |
|
903 aManager->SetUserData(&gLayerManagerLayerBuilder, this); |
|
904 } |
|
905 |
|
906 void |
|
907 FrameLayerBuilder::FlashPaint(gfxContext *aContext) |
|
908 { |
|
909 float r = float(rand()) / RAND_MAX; |
|
910 float g = float(rand()) / RAND_MAX; |
|
911 float b = float(rand()) / RAND_MAX; |
|
912 aContext->SetColor(gfxRGBA(r, g, b, 0.4)); |
|
913 aContext->Paint(); |
|
914 } |
|
915 |
|
916 FrameLayerBuilder::DisplayItemData* |
|
917 FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame, uint32_t aKey) |
|
918 { |
|
919 nsTArray<DisplayItemData*> *array = |
|
920 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
921 if (array) { |
|
922 for (uint32_t i = 0; i < array->Length(); i++) { |
|
923 DisplayItemData* item = array->ElementAt(i); |
|
924 if (item->mDisplayItemKey == aKey && |
|
925 item->mLayer->Manager() == mRetainingManager) { |
|
926 return item; |
|
927 } |
|
928 } |
|
929 } |
|
930 return nullptr; |
|
931 } |
|
932 |
|
933 nsACString& |
|
934 AppendToString(nsACString& s, const nsIntRect& r, |
|
935 const char* pfx="", const char* sfx="") |
|
936 { |
|
937 s += pfx; |
|
938 s += nsPrintfCString( |
|
939 "(x=%d, y=%d, w=%d, h=%d)", |
|
940 r.x, r.y, r.width, r.height); |
|
941 return s += sfx; |
|
942 } |
|
943 |
|
944 nsACString& |
|
945 AppendToString(nsACString& s, const nsIntRegion& r, |
|
946 const char* pfx="", const char* sfx="") |
|
947 { |
|
948 s += pfx; |
|
949 |
|
950 nsIntRegionRectIterator it(r); |
|
951 s += "< "; |
|
952 while (const nsIntRect* sr = it.Next()) { |
|
953 AppendToString(s, *sr) += "; "; |
|
954 } |
|
955 s += ">"; |
|
956 |
|
957 return s += sfx; |
|
958 } |
|
959 |
|
960 /** |
|
961 * Invalidate aRegion in aLayer. aLayer is in the coordinate system |
|
962 * *after* aTranslation has been applied, so we need to |
|
963 * apply the inverse of that transform before calling InvalidateRegion. |
|
964 */ |
|
965 static void |
|
966 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsIntRegion& aRegion, |
|
967 const nsIntPoint& aTranslation) |
|
968 { |
|
969 // Convert the region from the coordinates of the container layer |
|
970 // (relative to the snapped top-left of the display list reference frame) |
|
971 // to the ThebesLayer's own coordinates |
|
972 nsIntRegion rgn = aRegion; |
|
973 rgn.MoveBy(-aTranslation); |
|
974 aLayer->InvalidateRegion(rgn); |
|
975 #ifdef MOZ_DUMP_PAINTING |
|
976 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
977 nsAutoCString str; |
|
978 AppendToString(str, rgn); |
|
979 printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get()); |
|
980 } |
|
981 #endif |
|
982 } |
|
983 |
|
984 static void |
|
985 InvalidatePostTransformRegion(ThebesLayer* aLayer, const nsRect& aRect, |
|
986 const DisplayItemClip& aClip, |
|
987 const nsIntPoint& aTranslation) |
|
988 { |
|
989 ThebesDisplayItemLayerUserData* data = |
|
990 static_cast<ThebesDisplayItemLayerUserData*>(aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
991 |
|
992 nsRect rect = aClip.ApplyNonRoundedIntersection(aRect); |
|
993 |
|
994 nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel); |
|
995 InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation); |
|
996 } |
|
997 |
|
998 |
|
999 static nsIntPoint |
|
1000 GetTranslationForThebesLayer(ThebesLayer* aLayer) |
|
1001 { |
|
1002 ThebesDisplayItemLayerUserData* data = |
|
1003 static_cast<ThebesDisplayItemLayerUserData*> |
|
1004 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
1005 NS_ASSERTION(data, "Must be a tracked thebes layer!"); |
|
1006 |
|
1007 return data->mTranslation; |
|
1008 } |
|
1009 |
|
1010 /** |
|
1011 * Some frames can have multiple, nested, retaining layer managers |
|
1012 * associated with them (normal manager, inactive managers, SVG effects). |
|
1013 * In these cases we store the 'outermost' LayerManager data property |
|
1014 * on the frame since we can walk down the chain from there. |
|
1015 * |
|
1016 * If one of these frames has just been destroyed, we will free the inner |
|
1017 * layer manager when removing the entry from mFramesWithLayers. Destroying |
|
1018 * the layer manager destroys the LayerManagerData and calls into |
|
1019 * the DisplayItemData destructor. If the inner layer manager had any |
|
1020 * items with the same frame, then we attempt to retrieve properties |
|
1021 * from the deleted frame. |
|
1022 * |
|
1023 * Cache the destroyed frame pointer here so we can avoid crashing in this case. |
|
1024 */ |
|
1025 |
|
1026 /* static */ void |
|
1027 FrameLayerBuilder::RemoveFrameFromLayerManager(nsIFrame* aFrame, |
|
1028 void* aPropertyValue) |
|
1029 { |
|
1030 sDestroyedFrame = aFrame; |
|
1031 nsTArray<DisplayItemData*> *array = |
|
1032 reinterpret_cast<nsTArray<DisplayItemData*>*>(aPropertyValue); |
|
1033 |
|
1034 // Hold a reference to all the items so that they don't get |
|
1035 // deleted from under us. |
|
1036 nsTArray<nsRefPtr<DisplayItemData> > arrayCopy; |
|
1037 for (uint32_t i = 0; i < array->Length(); ++i) { |
|
1038 arrayCopy.AppendElement(array->ElementAt(i)); |
|
1039 } |
|
1040 |
|
1041 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
1042 if (array->Length()) { |
|
1043 LayerManagerData *rootData = array->ElementAt(0)->mParent; |
|
1044 while (rootData->mParent) { |
|
1045 rootData = rootData->mParent; |
|
1046 } |
|
1047 printf_stderr("Removing frame %p - dumping display data\n", aFrame); |
|
1048 rootData->Dump(); |
|
1049 } |
|
1050 #endif |
|
1051 |
|
1052 for (uint32_t i = 0; i < array->Length(); ++i) { |
|
1053 DisplayItemData* data = array->ElementAt(i); |
|
1054 |
|
1055 ThebesLayer* t = data->mLayer->AsThebesLayer(); |
|
1056 if (t) { |
|
1057 ThebesDisplayItemLayerUserData* thebesData = |
|
1058 static_cast<ThebesDisplayItemLayerUserData*>(t->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
1059 if (thebesData) { |
|
1060 nsRegion old = data->mGeometry->ComputeInvalidationRegion(); |
|
1061 nsIntRegion rgn = old.ScaleToOutsidePixels(thebesData->mXScale, thebesData->mYScale, thebesData->mAppUnitsPerDevPixel); |
|
1062 rgn.MoveBy(-GetTranslationForThebesLayer(t)); |
|
1063 thebesData->mRegionToInvalidate.Or(thebesData->mRegionToInvalidate, rgn); |
|
1064 thebesData->mRegionToInvalidate.SimplifyOutward(8); |
|
1065 } |
|
1066 } |
|
1067 |
|
1068 data->mParent->mDisplayItems.RemoveEntry(data); |
|
1069 } |
|
1070 |
|
1071 arrayCopy.Clear(); |
|
1072 delete array; |
|
1073 sDestroyedFrame = nullptr; |
|
1074 } |
|
1075 |
|
1076 void |
|
1077 FrameLayerBuilder::DidBeginRetainedLayerTransaction(LayerManager* aManager) |
|
1078 { |
|
1079 mRetainingManager = aManager; |
|
1080 LayerManagerData* data = static_cast<LayerManagerData*> |
|
1081 (aManager->GetUserData(&gLayerManagerUserData)); |
|
1082 if (data) { |
|
1083 mInvalidateAllLayers = data->mInvalidateAllLayers; |
|
1084 } else { |
|
1085 data = new LayerManagerData(aManager); |
|
1086 aManager->SetUserData(&gLayerManagerUserData, data); |
|
1087 } |
|
1088 } |
|
1089 |
|
1090 void |
|
1091 FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLayer) |
|
1092 { |
|
1093 if (!mRetainingManager) { |
|
1094 return; |
|
1095 } |
|
1096 |
|
1097 DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager()); |
|
1098 NS_ASSERTION(data, "Must have already stored data for this item!"); |
|
1099 data->mOptLayer = aLayer; |
|
1100 } |
|
1101 |
|
1102 void |
|
1103 FrameLayerBuilder::DidEndTransaction() |
|
1104 { |
|
1105 GetMaskLayerImageCache()->Sweep(); |
|
1106 } |
|
1107 |
|
1108 void |
|
1109 FrameLayerBuilder::WillEndTransaction() |
|
1110 { |
|
1111 if (!mRetainingManager) { |
|
1112 return; |
|
1113 } |
|
1114 |
|
1115 // We need to save the data we'll need to support retaining. |
|
1116 LayerManagerData* data = static_cast<LayerManagerData*> |
|
1117 (mRetainingManager->GetUserData(&gLayerManagerUserData)); |
|
1118 NS_ASSERTION(data, "Must have data!"); |
|
1119 // Update all the frames that used to have layers. |
|
1120 data->mDisplayItems.EnumerateEntries(ProcessRemovedDisplayItems, this); |
|
1121 data->mInvalidateAllLayers = false; |
|
1122 } |
|
1123 |
|
1124 /* static */ PLDHashOperator |
|
1125 FrameLayerBuilder::ProcessRemovedDisplayItems(nsRefPtrHashKey<DisplayItemData>* aEntry, |
|
1126 void* aUserArg) |
|
1127 { |
|
1128 DisplayItemData* data = aEntry->GetKey(); |
|
1129 if (!data->mUsed) { |
|
1130 // This item was visible, but isn't anymore. |
|
1131 FrameLayerBuilder* layerBuilder = static_cast<FrameLayerBuilder*>(aUserArg); |
|
1132 |
|
1133 ThebesLayer* t = data->mLayer->AsThebesLayer(); |
|
1134 if (t) { |
|
1135 #ifdef MOZ_DUMP_PAINTING |
|
1136 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
1137 printf_stderr("Invalidating unused display item (%i) belonging to frame %p from layer %p\n", data->mDisplayItemKey, data->mFrameList[0], t); |
|
1138 } |
|
1139 #endif |
|
1140 InvalidatePostTransformRegion(t, |
|
1141 data->mGeometry->ComputeInvalidationRegion(), |
|
1142 data->mClip, |
|
1143 layerBuilder->GetLastPaintOffset(t)); |
|
1144 } |
|
1145 return PL_DHASH_REMOVE; |
|
1146 } |
|
1147 |
|
1148 data->mUsed = false; |
|
1149 data->mIsInvalid = false; |
|
1150 return PL_DHASH_NEXT; |
|
1151 } |
|
1152 |
|
1153 /* static */ PLDHashOperator |
|
1154 FrameLayerBuilder::DumpDisplayItemDataForFrame(nsRefPtrHashKey<DisplayItemData>* aEntry, |
|
1155 void* aClosure) |
|
1156 { |
|
1157 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
1158 DisplayItemData *data = aEntry->GetKey(); |
|
1159 |
|
1160 nsAutoCString prefix; |
|
1161 prefix += static_cast<const char*>(aClosure); |
|
1162 |
|
1163 const char *layerState; |
|
1164 switch (data->mLayerState) { |
|
1165 case LAYER_NONE: |
|
1166 layerState = "LAYER_NONE"; break; |
|
1167 case LAYER_INACTIVE: |
|
1168 layerState = "LAYER_INACTIVE"; break; |
|
1169 case LAYER_ACTIVE: |
|
1170 layerState = "LAYER_ACTIVE"; break; |
|
1171 case LAYER_ACTIVE_FORCE: |
|
1172 layerState = "LAYER_ACTIVE_FORCE"; break; |
|
1173 case LAYER_ACTIVE_EMPTY: |
|
1174 layerState = "LAYER_ACTIVE_EMPTY"; break; |
|
1175 case LAYER_SVG_EFFECTS: |
|
1176 layerState = "LAYER_SVG_EFFECTS"; break; |
|
1177 } |
|
1178 uint32_t mask = (1 << nsDisplayItem::TYPE_BITS) - 1; |
|
1179 |
|
1180 nsAutoCString str; |
|
1181 str += prefix; |
|
1182 str += nsPrintfCString("Frame %p ", data->mFrameList[0]); |
|
1183 str += nsDisplayItem::DisplayItemTypeName(static_cast<nsDisplayItem::Type>(data->mDisplayItemKey & mask)); |
|
1184 if ((data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS)) { |
|
1185 str += nsPrintfCString("(%i)", data->mDisplayItemKey >> nsDisplayItem::TYPE_BITS); |
|
1186 } |
|
1187 str += nsPrintfCString(", %s, Layer %p", layerState, data->mLayer.get()); |
|
1188 if (data->mOptLayer) { |
|
1189 str += nsPrintfCString(", OptLayer %p", data->mOptLayer.get()); |
|
1190 } |
|
1191 if (data->mInactiveManager) { |
|
1192 str += nsPrintfCString(", InactiveLayerManager %p", data->mInactiveManager.get()); |
|
1193 } |
|
1194 str += "\n"; |
|
1195 |
|
1196 printf_stderr("%s", str.get()); |
|
1197 |
|
1198 if (data->mInactiveManager) { |
|
1199 prefix += " "; |
|
1200 printf_stderr("%sDumping inactive layer info:\n", prefix.get()); |
|
1201 LayerManagerData* lmd = static_cast<LayerManagerData*> |
|
1202 (data->mInactiveManager->GetUserData(&gLayerManagerUserData)); |
|
1203 lmd->Dump(prefix.get()); |
|
1204 } |
|
1205 #endif |
|
1206 return PL_DHASH_NEXT; |
|
1207 } |
|
1208 |
|
1209 /* static */ FrameLayerBuilder::DisplayItemData* |
|
1210 FrameLayerBuilder::GetDisplayItemDataForManager(nsDisplayItem* aItem, |
|
1211 LayerManager* aManager) |
|
1212 { |
|
1213 nsTArray<DisplayItemData*> *array = |
|
1214 reinterpret_cast<nsTArray<DisplayItemData*>*>(aItem->Frame()->Properties().Get(LayerManagerDataProperty())); |
|
1215 if (array) { |
|
1216 for (uint32_t i = 0; i < array->Length(); i++) { |
|
1217 DisplayItemData* item = array->ElementAt(i); |
|
1218 if (item->mDisplayItemKey == aItem->GetPerFrameKey() && |
|
1219 item->mLayer->Manager() == aManager) { |
|
1220 return item; |
|
1221 } |
|
1222 } |
|
1223 } |
|
1224 return nullptr; |
|
1225 } |
|
1226 |
|
1227 bool |
|
1228 FrameLayerBuilder::HasRetainedDataFor(nsIFrame* aFrame, uint32_t aDisplayItemKey) |
|
1229 { |
|
1230 nsTArray<DisplayItemData*> *array = |
|
1231 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
1232 if (array) { |
|
1233 for (uint32_t i = 0; i < array->Length(); i++) { |
|
1234 if (array->ElementAt(i)->mDisplayItemKey == aDisplayItemKey) { |
|
1235 return true; |
|
1236 } |
|
1237 } |
|
1238 } |
|
1239 return false; |
|
1240 } |
|
1241 |
|
1242 void |
|
1243 FrameLayerBuilder::IterateRetainedDataFor(nsIFrame* aFrame, DisplayItemDataCallback aCallback) |
|
1244 { |
|
1245 nsTArray<DisplayItemData*> *array = |
|
1246 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
1247 if (!array) { |
|
1248 return; |
|
1249 } |
|
1250 |
|
1251 for (uint32_t i = 0; i < array->Length(); i++) { |
|
1252 DisplayItemData* data = array->ElementAt(i); |
|
1253 if (data->mDisplayItemKey != nsDisplayItem::TYPE_ZERO) { |
|
1254 aCallback(aFrame, data); |
|
1255 } |
|
1256 } |
|
1257 } |
|
1258 |
|
1259 FrameLayerBuilder::DisplayItemData* |
|
1260 FrameLayerBuilder::GetOldLayerForFrame(nsIFrame* aFrame, uint32_t aDisplayItemKey) |
|
1261 { |
|
1262 // If we need to build a new layer tree, then just refuse to recycle |
|
1263 // anything. |
|
1264 if (!mRetainingManager || mInvalidateAllLayers) |
|
1265 return nullptr; |
|
1266 |
|
1267 DisplayItemData *data = GetDisplayItemData(aFrame, aDisplayItemKey); |
|
1268 |
|
1269 if (data && data->mLayer->Manager() == mRetainingManager) { |
|
1270 return data; |
|
1271 } |
|
1272 return nullptr; |
|
1273 } |
|
1274 |
|
1275 Layer* |
|
1276 FrameLayerBuilder::GetOldLayerFor(nsDisplayItem* aItem, |
|
1277 nsDisplayItemGeometry** aOldGeometry, |
|
1278 DisplayItemClip** aOldClip, |
|
1279 nsTArray<nsIFrame*>* aChangedFrames, |
|
1280 bool *aIsInvalid) |
|
1281 { |
|
1282 uint32_t key = aItem->GetPerFrameKey(); |
|
1283 nsIFrame* frame = aItem->Frame(); |
|
1284 |
|
1285 DisplayItemData* oldData = GetOldLayerForFrame(frame, key); |
|
1286 if (oldData) { |
|
1287 if (aOldGeometry) { |
|
1288 *aOldGeometry = oldData->mGeometry.get(); |
|
1289 } |
|
1290 if (aOldClip) { |
|
1291 *aOldClip = &oldData->mClip; |
|
1292 } |
|
1293 if (aChangedFrames) { |
|
1294 oldData->GetFrameListChanges(aItem, *aChangedFrames); |
|
1295 } |
|
1296 if (aIsInvalid) { |
|
1297 *aIsInvalid = oldData->mIsInvalid; |
|
1298 } |
|
1299 return oldData->mLayer; |
|
1300 } |
|
1301 |
|
1302 return nullptr; |
|
1303 } |
|
1304 |
|
1305 /* static */ Layer* |
|
1306 FrameLayerBuilder::GetDebugOldLayerFor(nsIFrame* aFrame, uint32_t aDisplayItemKey) |
|
1307 { |
|
1308 nsTArray<DisplayItemData*> *array = |
|
1309 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
1310 |
|
1311 if (!array) { |
|
1312 return nullptr; |
|
1313 } |
|
1314 |
|
1315 for (uint32_t i = 0; i < array->Length(); i++) { |
|
1316 DisplayItemData *data = array->ElementAt(i); |
|
1317 |
|
1318 if (data->mDisplayItemKey == aDisplayItemKey) { |
|
1319 return data->mLayer; |
|
1320 } |
|
1321 } |
|
1322 return nullptr; |
|
1323 } |
|
1324 |
|
1325 already_AddRefed<ColorLayer> |
|
1326 ContainerState::CreateOrRecycleColorLayer(ThebesLayer *aThebes) |
|
1327 { |
|
1328 ThebesDisplayItemLayerUserData* data = |
|
1329 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
1330 nsRefPtr<ColorLayer> layer = data->mColorLayer; |
|
1331 if (layer) { |
|
1332 layer->SetMaskLayer(nullptr); |
|
1333 } else { |
|
1334 // Create a new layer |
|
1335 layer = mManager->CreateColorLayer(); |
|
1336 if (!layer) |
|
1337 return nullptr; |
|
1338 // Mark this layer as being used for Thebes-painting display items |
|
1339 data->mColorLayer = layer; |
|
1340 layer->SetUserData(&gColorLayerUserData, nullptr); |
|
1341 |
|
1342 // Remove other layer types we might have stored for this ThebesLayer |
|
1343 data->mImageLayer = nullptr; |
|
1344 } |
|
1345 return layer.forget(); |
|
1346 } |
|
1347 |
|
1348 already_AddRefed<ImageLayer> |
|
1349 ContainerState::CreateOrRecycleImageLayer(ThebesLayer *aThebes) |
|
1350 { |
|
1351 ThebesDisplayItemLayerUserData* data = |
|
1352 static_cast<ThebesDisplayItemLayerUserData*>(aThebes->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
1353 nsRefPtr<ImageLayer> layer = data->mImageLayer; |
|
1354 if (layer) { |
|
1355 layer->SetMaskLayer(nullptr); |
|
1356 } else { |
|
1357 // Create a new layer |
|
1358 layer = mManager->CreateImageLayer(); |
|
1359 if (!layer) |
|
1360 return nullptr; |
|
1361 // Mark this layer as being used for Thebes-painting display items |
|
1362 data->mImageLayer = layer; |
|
1363 layer->SetUserData(&gImageLayerUserData, nullptr); |
|
1364 |
|
1365 // Remove other layer types we might have stored for this ThebesLayer |
|
1366 data->mColorLayer = nullptr; |
|
1367 } |
|
1368 return layer.forget(); |
|
1369 } |
|
1370 |
|
1371 already_AddRefed<ImageLayer> |
|
1372 ContainerState::CreateOrRecycleMaskImageLayerFor(Layer* aLayer) |
|
1373 { |
|
1374 nsRefPtr<ImageLayer> result = mRecycledMaskImageLayers.Get(aLayer); |
|
1375 if (result) { |
|
1376 mRecycledMaskImageLayers.Remove(aLayer); |
|
1377 // XXX if we use clip on mask layers, null it out here |
|
1378 } else { |
|
1379 // Create a new layer |
|
1380 result = mManager->CreateImageLayer(); |
|
1381 if (!result) |
|
1382 return nullptr; |
|
1383 result->SetUserData(&gMaskLayerUserData, new MaskLayerUserData()); |
|
1384 result->SetDisallowBigImage(true); |
|
1385 } |
|
1386 |
|
1387 return result.forget(); |
|
1388 } |
|
1389 |
|
1390 static const double SUBPIXEL_OFFSET_EPSILON = 0.02; |
|
1391 |
|
1392 /** |
|
1393 * This normally computes NSToIntRoundUp(aValue). However, if that would |
|
1394 * give a residual near 0.5 while aOldResidual is near -0.5, or |
|
1395 * it would give a residual near -0.5 while aOldResidual is near 0.5, then |
|
1396 * instead we return the integer in the other direction so that the residual |
|
1397 * is close to aOldResidual. |
|
1398 */ |
|
1399 static int32_t |
|
1400 RoundToMatchResidual(double aValue, double aOldResidual) |
|
1401 { |
|
1402 int32_t v = NSToIntRoundUp(aValue); |
|
1403 double residual = aValue - v; |
|
1404 if (aOldResidual < 0) { |
|
1405 if (residual > 0 && fabs(residual - 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) { |
|
1406 // Round up instead |
|
1407 return int32_t(ceil(aValue)); |
|
1408 } |
|
1409 } else if (aOldResidual > 0) { |
|
1410 if (residual < 0 && fabs(residual + 1.0 - aOldResidual) < SUBPIXEL_OFFSET_EPSILON) { |
|
1411 // Round down instead |
|
1412 return int32_t(floor(aValue)); |
|
1413 } |
|
1414 } |
|
1415 return v; |
|
1416 } |
|
1417 |
|
1418 static void |
|
1419 ResetScrollPositionForLayerPixelAlignment(const nsIFrame* aAnimatedGeometryRoot) |
|
1420 { |
|
1421 nsIScrollableFrame* sf = nsLayoutUtils::GetScrollableFrameFor(aAnimatedGeometryRoot); |
|
1422 if (sf) { |
|
1423 sf->ResetScrollPositionForLayerPixelAlignment(); |
|
1424 } |
|
1425 } |
|
1426 |
|
1427 static void |
|
1428 InvalidateEntireThebesLayer(ThebesLayer* aLayer, const nsIFrame* aAnimatedGeometryRoot) |
|
1429 { |
|
1430 #ifdef MOZ_DUMP_PAINTING |
|
1431 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
1432 printf_stderr("Invalidating entire layer %p\n", aLayer); |
|
1433 } |
|
1434 #endif |
|
1435 nsIntRect invalidate = aLayer->GetValidRegion().GetBounds(); |
|
1436 aLayer->InvalidateRegion(invalidate); |
|
1437 aLayer->SetInvalidRectToVisibleRegion(); |
|
1438 ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot); |
|
1439 } |
|
1440 |
|
1441 already_AddRefed<ThebesLayer> |
|
1442 ContainerState::CreateOrRecycleThebesLayer(const nsIFrame* aAnimatedGeometryRoot, |
|
1443 const nsIFrame* aReferenceFrame, |
|
1444 const nsPoint& aTopLeft) |
|
1445 { |
|
1446 // We need a new thebes layer |
|
1447 nsRefPtr<ThebesLayer> layer; |
|
1448 ThebesDisplayItemLayerUserData* data; |
|
1449 #ifndef MOZ_ANDROID_OMTC |
|
1450 bool didResetScrollPositionForLayerPixelAlignment = false; |
|
1451 #endif |
|
1452 if (mNextFreeRecycledThebesLayer < mRecycledThebesLayers.Length()) { |
|
1453 // Recycle a layer |
|
1454 layer = mRecycledThebesLayers[mNextFreeRecycledThebesLayer]; |
|
1455 ++mNextFreeRecycledThebesLayer; |
|
1456 // Clear clip rect and mask layer so we don't accidentally stay clipped. |
|
1457 // We will reapply any necessary clipping. |
|
1458 layer->SetMaskLayer(nullptr); |
|
1459 |
|
1460 data = static_cast<ThebesDisplayItemLayerUserData*> |
|
1461 (layer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
1462 NS_ASSERTION(data, "Recycled ThebesLayers must have user data"); |
|
1463 |
|
1464 // This gets called on recycled ThebesLayers that are going to be in the |
|
1465 // final layer tree, so it's a convenient time to invalidate the |
|
1466 // content that changed where we don't know what ThebesLayer it belonged |
|
1467 // to, or if we need to invalidate the entire layer, we can do that. |
|
1468 // This needs to be done before we update the ThebesLayer to its new |
|
1469 // transform. See nsGfxScrollFrame::InvalidateInternal, where |
|
1470 // we ensure that mInvalidThebesContent is updated according to the |
|
1471 // scroll position as of the most recent paint. |
|
1472 if (!FuzzyEqual(data->mXScale, mParameters.mXScale, 0.00001f) || |
|
1473 !FuzzyEqual(data->mYScale, mParameters.mYScale, 0.00001f) || |
|
1474 data->mAppUnitsPerDevPixel != mAppUnitsPerDevPixel) { |
|
1475 InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot); |
|
1476 #ifndef MOZ_ANDROID_OMTC |
|
1477 didResetScrollPositionForLayerPixelAlignment = true; |
|
1478 #endif |
|
1479 } |
|
1480 if (!data->mRegionToInvalidate.IsEmpty()) { |
|
1481 #ifdef MOZ_DUMP_PAINTING |
|
1482 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
1483 printf_stderr("Invalidating deleted frame content from layer %p\n", layer.get()); |
|
1484 } |
|
1485 #endif |
|
1486 layer->InvalidateRegion(data->mRegionToInvalidate); |
|
1487 #ifdef MOZ_DUMP_PAINTING |
|
1488 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
1489 nsAutoCString str; |
|
1490 AppendToString(str, data->mRegionToInvalidate); |
|
1491 printf_stderr("Invalidating layer %p: %s\n", layer.get(), str.get()); |
|
1492 } |
|
1493 #endif |
|
1494 data->mRegionToInvalidate.SetEmpty(); |
|
1495 } |
|
1496 |
|
1497 // We do not need to Invalidate these areas in the widget because we |
|
1498 // assume the caller of InvalidateThebesLayerContents has ensured |
|
1499 // the area is invalidated in the widget. |
|
1500 } else { |
|
1501 // Check whether the layer will be scrollable. This is used as a hint to |
|
1502 // influence whether tiled layers are used or not. |
|
1503 bool canScroll = false; |
|
1504 nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent(); |
|
1505 if (animatedGeometryRootParent && |
|
1506 animatedGeometryRootParent->GetType() == nsGkAtoms::scrollFrame) { |
|
1507 canScroll = true; |
|
1508 } |
|
1509 // Create a new thebes layer |
|
1510 layer = mManager->CreateThebesLayerWithHint(canScroll ? LayerManager::SCROLLABLE : |
|
1511 LayerManager::NONE); |
|
1512 if (!layer) |
|
1513 return nullptr; |
|
1514 // Mark this layer as being used for Thebes-painting display items |
|
1515 data = new ThebesDisplayItemLayerUserData(); |
|
1516 layer->SetUserData(&gThebesDisplayItemLayerUserData, data); |
|
1517 ResetScrollPositionForLayerPixelAlignment(aAnimatedGeometryRoot); |
|
1518 #ifndef MOZ_ANDROID_OMTC |
|
1519 didResetScrollPositionForLayerPixelAlignment = true; |
|
1520 #endif |
|
1521 } |
|
1522 data->mXScale = mParameters.mXScale; |
|
1523 data->mYScale = mParameters.mYScale; |
|
1524 data->mLastAnimatedGeometryRootOrigin = data->mAnimatedGeometryRootOrigin; |
|
1525 data->mAnimatedGeometryRootOrigin = aTopLeft; |
|
1526 data->mAppUnitsPerDevPixel = mAppUnitsPerDevPixel; |
|
1527 layer->SetAllowResidualTranslation(mParameters.AllowResidualTranslation()); |
|
1528 |
|
1529 mLayerBuilder->SaveLastPaintOffset(layer); |
|
1530 |
|
1531 // Set up transform so that 0,0 in the Thebes layer corresponds to the |
|
1532 // (pixel-snapped) top-left of the aAnimatedGeometryRoot. |
|
1533 nsPoint offset = aAnimatedGeometryRoot->GetOffsetToCrossDoc(aReferenceFrame); |
|
1534 nscoord appUnitsPerDevPixel = aAnimatedGeometryRoot->PresContext()->AppUnitsPerDevPixel(); |
|
1535 gfxPoint scaledOffset( |
|
1536 NSAppUnitsToDoublePixels(offset.x, appUnitsPerDevPixel)*mParameters.mXScale, |
|
1537 NSAppUnitsToDoublePixels(offset.y, appUnitsPerDevPixel)*mParameters.mYScale); |
|
1538 // We call RoundToMatchResidual here so that the residual after rounding |
|
1539 // is close to data->mAnimatedGeometryRootPosition if possible. |
|
1540 nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, data->mAnimatedGeometryRootPosition.x), |
|
1541 RoundToMatchResidual(scaledOffset.y, data->mAnimatedGeometryRootPosition.y)); |
|
1542 data->mTranslation = pixOffset; |
|
1543 pixOffset += mParameters.mOffset; |
|
1544 Matrix matrix; |
|
1545 matrix.Translate(pixOffset.x, pixOffset.y); |
|
1546 layer->SetBaseTransform(Matrix4x4::From2D(matrix)); |
|
1547 |
|
1548 // FIXME: Temporary workaround for bug 681192 and bug 724786. |
|
1549 #ifndef MOZ_ANDROID_OMTC |
|
1550 // Calculate exact position of the top-left of the active scrolled root. |
|
1551 // This might not be 0,0 due to the snapping in ScaleToNearestPixels. |
|
1552 gfxPoint animatedGeometryRootTopLeft = scaledOffset - ThebesPoint(matrix.GetTranslation()) + mParameters.mOffset; |
|
1553 // If it has changed, then we need to invalidate the entire layer since the |
|
1554 // pixels in the layer buffer have the content at a (subpixel) offset |
|
1555 // from what we need. |
|
1556 if (!animatedGeometryRootTopLeft.WithinEpsilonOf(data->mAnimatedGeometryRootPosition, SUBPIXEL_OFFSET_EPSILON)) { |
|
1557 data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft; |
|
1558 InvalidateEntireThebesLayer(layer, aAnimatedGeometryRoot); |
|
1559 } else if (didResetScrollPositionForLayerPixelAlignment) { |
|
1560 data->mAnimatedGeometryRootPosition = animatedGeometryRootTopLeft; |
|
1561 } |
|
1562 #endif |
|
1563 |
|
1564 return layer.forget(); |
|
1565 } |
|
1566 |
|
1567 #if defined(DEBUG) || defined(MOZ_DUMP_PAINTING) |
|
1568 /** |
|
1569 * Returns the appunits per dev pixel for the item's frame |
|
1570 */ |
|
1571 static int32_t |
|
1572 AppUnitsPerDevPixel(nsDisplayItem* aItem) |
|
1573 { |
|
1574 // The underlying frame for zoom items is the root frame of the subdocument. |
|
1575 // But zoom display items report their bounds etc using the parent document's |
|
1576 // APD because zoom items act as a conversion layer between the two different |
|
1577 // APDs. |
|
1578 if (aItem->GetType() == nsDisplayItem::TYPE_ZOOM) { |
|
1579 return static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel(); |
|
1580 } |
|
1581 return aItem->Frame()->PresContext()->AppUnitsPerDevPixel(); |
|
1582 } |
|
1583 #endif |
|
1584 |
|
1585 /** |
|
1586 * Restrict the visible region of aLayer to the region that is actually visible. |
|
1587 * Because we only reduce the visible region here, we don't need to worry |
|
1588 * about whether CONTENT_OPAQUE is set; if layer was opaque in the old |
|
1589 * visible region, it will still be opaque in the new one. |
|
1590 * @param aLayerVisibleRegion the visible region of the layer, in the layer's |
|
1591 * coordinate space |
|
1592 * @param aRestrictToRect the rect to restrict the visible region to, in the |
|
1593 * parent's coordinate system |
|
1594 */ |
|
1595 static void |
|
1596 SetVisibleRegionForLayer(Layer* aLayer, const nsIntRegion& aLayerVisibleRegion, |
|
1597 const nsIntRect& aRestrictToRect) |
|
1598 { |
|
1599 gfx3DMatrix transform; |
|
1600 To3DMatrix(aLayer->GetTransform(), transform); |
|
1601 |
|
1602 // if 'transform' is not invertible, then nothing will be displayed |
|
1603 // for the layer, so it doesn't really matter what we do here |
|
1604 gfxRect itemVisible(aRestrictToRect.x, aRestrictToRect.y, |
|
1605 aRestrictToRect.width, aRestrictToRect.height); |
|
1606 nsIntRect childBounds = aLayerVisibleRegion.GetBounds(); |
|
1607 gfxRect childGfxBounds(childBounds.x, childBounds.y, |
|
1608 childBounds.width, childBounds.height); |
|
1609 gfxRect layerVisible = transform.UntransformBounds(itemVisible, childGfxBounds); |
|
1610 layerVisible.RoundOut(); |
|
1611 |
|
1612 nsIntRect visibleRect; |
|
1613 if (!gfxUtils::GfxRectToIntRect(layerVisible, &visibleRect)) { |
|
1614 aLayer->SetVisibleRegion(nsIntRegion()); |
|
1615 } else { |
|
1616 nsIntRegion rgn; |
|
1617 rgn.And(aLayerVisibleRegion, visibleRect); |
|
1618 aLayer->SetVisibleRegion(rgn); |
|
1619 } |
|
1620 } |
|
1621 |
|
1622 nscolor |
|
1623 ContainerState::FindOpaqueBackgroundColorFor(int32_t aThebesLayerIndex) |
|
1624 { |
|
1625 ThebesLayerData* target = mThebesLayerDataStack[aThebesLayerIndex]; |
|
1626 for (int32_t i = aThebesLayerIndex - 1; i >= 0; --i) { |
|
1627 ThebesLayerData* candidate = mThebesLayerDataStack[i]; |
|
1628 if (candidate->IntersectsVisibleAboveRegion(target->mVisibleRegion)) { |
|
1629 // Some non-Thebes content between target and candidate; this is |
|
1630 // hopeless |
|
1631 break; |
|
1632 } |
|
1633 |
|
1634 nsIntRegion intersection; |
|
1635 intersection.And(candidate->mVisibleRegion, target->mVisibleRegion); |
|
1636 if (intersection.IsEmpty()) { |
|
1637 // The layer doesn't intersect our target, ignore it and move on |
|
1638 continue; |
|
1639 } |
|
1640 |
|
1641 // The candidate intersects our target. If any layer has a solid-color |
|
1642 // area behind our target, this must be it. Scan its display items. |
|
1643 nsIntRect deviceRect = target->mVisibleRegion.GetBounds(); |
|
1644 nsRect appUnitRect = deviceRect.ToAppUnits(mAppUnitsPerDevPixel); |
|
1645 appUnitRect.ScaleInverseRoundOut(mParameters.mXScale, mParameters.mYScale); |
|
1646 |
|
1647 FrameLayerBuilder::ThebesLayerItemsEntry* entry = |
|
1648 mLayerBuilder->GetThebesLayerItemsEntry(candidate->mLayer); |
|
1649 NS_ASSERTION(entry, "Must know about this layer!"); |
|
1650 for (int32_t j = entry->mItems.Length() - 1; j >= 0; --j) { |
|
1651 nsDisplayItem* item = entry->mItems[j].mItem; |
|
1652 bool snap; |
|
1653 nsRect bounds = item->GetBounds(mBuilder, &snap); |
|
1654 if (snap && mSnappingEnabled) { |
|
1655 nsIntRect snappedBounds = ScaleToNearestPixels(bounds); |
|
1656 if (!snappedBounds.Intersects(deviceRect)) |
|
1657 continue; |
|
1658 |
|
1659 if (!snappedBounds.Contains(deviceRect)) |
|
1660 break; |
|
1661 |
|
1662 } else { |
|
1663 // The layer's visible rect is already (close enough to) pixel |
|
1664 // aligned, so no need to round out and in here. |
|
1665 if (!bounds.Intersects(appUnitRect)) |
|
1666 continue; |
|
1667 |
|
1668 if (!bounds.Contains(appUnitRect)) |
|
1669 break; |
|
1670 } |
|
1671 |
|
1672 nscolor color; |
|
1673 if (item->IsUniform(mBuilder, &color) && NS_GET_A(color) == 255) |
|
1674 return color; |
|
1675 |
|
1676 break; |
|
1677 } |
|
1678 break; |
|
1679 } |
|
1680 return NS_RGBA(0,0,0,0); |
|
1681 } |
|
1682 |
|
1683 void |
|
1684 ThebesLayerData::UpdateCommonClipCount( |
|
1685 const DisplayItemClip& aCurrentClip) |
|
1686 { |
|
1687 if (mCommonClipCount >= 0) { |
|
1688 mCommonClipCount = mItemClip.GetCommonRoundedRectCount(aCurrentClip, mCommonClipCount); |
|
1689 } else { |
|
1690 // first item in the layer |
|
1691 mCommonClipCount = aCurrentClip.GetRoundedRectCount(); |
|
1692 } |
|
1693 } |
|
1694 |
|
1695 already_AddRefed<ImageContainer> |
|
1696 ThebesLayerData::CanOptimizeImageLayer(nsDisplayListBuilder* aBuilder) |
|
1697 { |
|
1698 if (!mImage) { |
|
1699 return nullptr; |
|
1700 } |
|
1701 |
|
1702 return mImage->GetContainer(mLayer->Manager(), aBuilder); |
|
1703 } |
|
1704 |
|
1705 const nsIFrame* |
|
1706 ContainerState::FindFixedPosFrameForLayerData(const nsIFrame* aAnimatedGeometryRoot, |
|
1707 bool aDisplayItemFixedToViewport) |
|
1708 { |
|
1709 if (!mManager->IsWidgetLayerManager()) { |
|
1710 // Never attach any fixed-pos metadata to inactive layers, it's pointless! |
|
1711 return nullptr; |
|
1712 } |
|
1713 |
|
1714 nsPresContext* presContext = mContainerFrame->PresContext(); |
|
1715 nsIFrame* viewport = presContext->PresShell()->GetRootFrame(); |
|
1716 |
|
1717 if (viewport == aAnimatedGeometryRoot && aDisplayItemFixedToViewport && |
|
1718 nsLayoutUtils::ViewportHasDisplayPort(presContext)) { |
|
1719 // Probably a background-attachment:fixed item |
|
1720 return viewport; |
|
1721 } |
|
1722 // Viewports with no fixed-pos frames are not relevant. |
|
1723 if (!viewport->GetFirstChild(nsIFrame::kFixedList)) { |
|
1724 return nullptr; |
|
1725 } |
|
1726 for (const nsIFrame* f = aAnimatedGeometryRoot; f; f = f->GetParent()) { |
|
1727 if (nsLayoutUtils::IsFixedPosFrameInDisplayPort(f)) { |
|
1728 return f; |
|
1729 } |
|
1730 if (f == mContainerReferenceFrame) { |
|
1731 // The metadata will go on an ancestor layer if necessary. |
|
1732 return nullptr; |
|
1733 } |
|
1734 } |
|
1735 return nullptr; |
|
1736 } |
|
1737 |
|
1738 void |
|
1739 ContainerState::AdjustLayerDataForFixedPositioning(const nsIFrame* aFixedPosFrame, |
|
1740 const nsIntRegion& aDrawRegion, |
|
1741 nsIntRegion* aVisibleRegion, |
|
1742 bool* aIsSolidColorInVisibleRegion) |
|
1743 { |
|
1744 if (!aFixedPosFrame) { |
|
1745 return; |
|
1746 } |
|
1747 |
|
1748 nsRect fixedVisibleRect; |
|
1749 nsPresContext* presContext = aFixedPosFrame->PresContext(); |
|
1750 nsIPresShell* presShell = presContext->PresShell(); |
|
1751 DebugOnly<bool> hasDisplayPort = |
|
1752 nsLayoutUtils::ViewportHasDisplayPort(presContext, &fixedVisibleRect); |
|
1753 NS_ASSERTION(hasDisplayPort, "No fixed-pos layer data if there's no displayport"); |
|
1754 // Display ports are relative to the viewport, convert it to be relative |
|
1755 // to our reference frame. |
|
1756 nsIFrame* viewport = presShell->GetRootFrame(); |
|
1757 if (aFixedPosFrame != viewport) { |
|
1758 // position: fixed items are reflowed into and only drawn inside the |
|
1759 // viewport, or the scroll position clamping scrollport size, if one is |
|
1760 // set. We differentiate background-attachment: fixed items from |
|
1761 // position: fixed items by the fact that background-attachment: fixed |
|
1762 // items use the viewport as their aFixedPosFrame. |
|
1763 NS_ASSERTION(aFixedPosFrame->StyleDisplay()->mPosition == NS_STYLE_POSITION_FIXED, |
|
1764 "should be position fixed items only"); |
|
1765 fixedVisibleRect.MoveTo(0, 0); |
|
1766 if (presShell->IsScrollPositionClampingScrollPortSizeSet()) { |
|
1767 fixedVisibleRect.SizeTo(presShell->GetScrollPositionClampingScrollPortSize()); |
|
1768 } else { |
|
1769 fixedVisibleRect.SizeTo(viewport->GetSize()); |
|
1770 } |
|
1771 } |
|
1772 fixedVisibleRect += viewport->GetOffsetToCrossDoc(mContainerReferenceFrame); |
|
1773 nsIntRegion newVisibleRegion; |
|
1774 newVisibleRegion.And(ScaleToOutsidePixels(fixedVisibleRect, false), |
|
1775 aDrawRegion); |
|
1776 if (!aVisibleRegion->Contains(newVisibleRegion)) { |
|
1777 if (aIsSolidColorInVisibleRegion) { |
|
1778 *aIsSolidColorInVisibleRegion = false; |
|
1779 } |
|
1780 *aVisibleRegion = newVisibleRegion; |
|
1781 } |
|
1782 } |
|
1783 |
|
1784 void |
|
1785 ContainerState::SetFixedPositionLayerData(Layer* aLayer, |
|
1786 const nsIFrame* aFixedPosFrame) |
|
1787 { |
|
1788 aLayer->SetIsFixedPosition(aFixedPosFrame != nullptr); |
|
1789 if (!aFixedPosFrame) { |
|
1790 return; |
|
1791 } |
|
1792 |
|
1793 nsPresContext* presContext = aFixedPosFrame->PresContext(); |
|
1794 |
|
1795 const nsIFrame* viewportFrame = aFixedPosFrame->GetParent(); |
|
1796 // anchorRect will be in the container's coordinate system (aLayer's parent layer). |
|
1797 // This is the same as the display items' reference frame. |
|
1798 nsRect anchorRect; |
|
1799 if (viewportFrame) { |
|
1800 // Fixed position frames are reflowed into the scroll-port size if one has |
|
1801 // been set. |
|
1802 if (presContext->PresShell()->IsScrollPositionClampingScrollPortSizeSet()) { |
|
1803 anchorRect.SizeTo(presContext->PresShell()->GetScrollPositionClampingScrollPortSize()); |
|
1804 } else { |
|
1805 anchorRect.SizeTo(viewportFrame->GetSize()); |
|
1806 } |
|
1807 } else { |
|
1808 // A display item directly attached to the viewport. |
|
1809 // For background-attachment:fixed items, the anchor point is always the |
|
1810 // top-left of the viewport currently. |
|
1811 viewportFrame = aFixedPosFrame; |
|
1812 } |
|
1813 // The anchorRect top-left is always the viewport top-left. |
|
1814 anchorRect.MoveTo(viewportFrame->GetOffsetToCrossDoc(mContainerReferenceFrame)); |
|
1815 |
|
1816 nsLayoutUtils::SetFixedPositionLayerData(aLayer, |
|
1817 viewportFrame, anchorRect, aFixedPosFrame, presContext, mParameters); |
|
1818 } |
|
1819 |
|
1820 static gfx3DMatrix |
|
1821 GetTransformToRoot(Layer* aLayer) |
|
1822 { |
|
1823 Matrix4x4 transform = aLayer->GetTransform(); |
|
1824 for (Layer* l = aLayer->GetParent(); l; l = l->GetParent()) { |
|
1825 transform = transform * l->GetTransform(); |
|
1826 } |
|
1827 gfx3DMatrix result; |
|
1828 To3DMatrix(transform, result); |
|
1829 return result; |
|
1830 } |
|
1831 |
|
1832 static void |
|
1833 AddTransformedBoundsToRegion(const nsIntRegion& aRegion, |
|
1834 const gfx3DMatrix& aTransform, |
|
1835 nsIntRegion* aDest) |
|
1836 { |
|
1837 nsIntRect bounds = aRegion.GetBounds(); |
|
1838 gfxRect transformed = |
|
1839 aTransform.TransformBounds(gfxRect(bounds.x, bounds.y, bounds.width, bounds.height)); |
|
1840 transformed.RoundOut(); |
|
1841 nsIntRect intRect; |
|
1842 if (!gfxUtils::GfxRectToIntRect(transformed, &intRect)) { |
|
1843 // This should only fail if coordinates are too big to fit in an int32 |
|
1844 *aDest = nsIntRect(-INT32_MAX/2, -INT32_MAX/2, INT32_MAX, INT32_MAX); |
|
1845 return; |
|
1846 } |
|
1847 aDest->Or(*aDest, intRect); |
|
1848 } |
|
1849 |
|
1850 static bool |
|
1851 CanOptimizeAwayThebesLayer(ThebesLayerData* aData, |
|
1852 FrameLayerBuilder* aLayerBuilder) |
|
1853 { |
|
1854 bool isRetained = aData->mLayer->Manager()->IsWidgetLayerManager(); |
|
1855 if (!isRetained) { |
|
1856 return false; |
|
1857 } |
|
1858 |
|
1859 // If there's no thebes layer with valid content in it that we can reuse, |
|
1860 // always create a color or image layer (and potentially throw away an |
|
1861 // existing completely invalid thebes layer). |
|
1862 if (aData->mLayer->GetValidRegion().IsEmpty()) { |
|
1863 return true; |
|
1864 } |
|
1865 |
|
1866 // There is an existing thebes layer we can reuse. Throwing it away can make |
|
1867 // compositing cheaper (see bug 946952), but it might cause us to re-allocate |
|
1868 // the thebes layer frequently due to an animation. So we only discard it if |
|
1869 // we're in tree compression mode, which is triggered at a low frequency. |
|
1870 return aLayerBuilder->CheckInLayerTreeCompressionMode(); |
|
1871 } |
|
1872 |
|
1873 void |
|
1874 ContainerState::PopThebesLayerData() |
|
1875 { |
|
1876 NS_ASSERTION(!mThebesLayerDataStack.IsEmpty(), "Can't pop"); |
|
1877 |
|
1878 int32_t lastIndex = mThebesLayerDataStack.Length() - 1; |
|
1879 ThebesLayerData* data = mThebesLayerDataStack[lastIndex]; |
|
1880 |
|
1881 AdjustLayerDataForFixedPositioning(data->mFixedPosFrameForLayerData, |
|
1882 data->mDrawRegion, |
|
1883 &data->mVisibleRegion, |
|
1884 &data->mIsSolidColorInVisibleRegion); |
|
1885 nsRefPtr<Layer> layer; |
|
1886 nsRefPtr<ImageContainer> imageContainer = data->CanOptimizeImageLayer(mBuilder); |
|
1887 |
|
1888 if ((data->mIsSolidColorInVisibleRegion || imageContainer) && |
|
1889 CanOptimizeAwayThebesLayer(data, mLayerBuilder)) { |
|
1890 NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer), |
|
1891 "Can't be a solid color as well as an image!"); |
|
1892 if (imageContainer) { |
|
1893 nsRefPtr<ImageLayer> imageLayer = CreateOrRecycleImageLayer(data->mLayer); |
|
1894 imageLayer->SetContainer(imageContainer); |
|
1895 data->mImage->ConfigureLayer(imageLayer, mParameters.mOffset); |
|
1896 imageLayer->SetPostScale(mParameters.mXScale, |
|
1897 mParameters.mYScale); |
|
1898 if (data->mItemClip.HasClip()) { |
|
1899 nsIntRect clip = ScaleToNearestPixels(data->mItemClip.GetClipRect()); |
|
1900 clip.MoveBy(mParameters.mOffset); |
|
1901 imageLayer->SetClipRect(&clip); |
|
1902 } else { |
|
1903 imageLayer->SetClipRect(nullptr); |
|
1904 } |
|
1905 layer = imageLayer; |
|
1906 mLayerBuilder->StoreOptimizedLayerForFrame(data->mImage, |
|
1907 imageLayer); |
|
1908 } else { |
|
1909 nsRefPtr<ColorLayer> colorLayer = CreateOrRecycleColorLayer(data->mLayer); |
|
1910 colorLayer->SetColor(data->mSolidColor); |
|
1911 |
|
1912 // Copy transform |
|
1913 colorLayer->SetBaseTransform(data->mLayer->GetBaseTransform()); |
|
1914 colorLayer->SetPostScale(data->mLayer->GetPostXScale(), data->mLayer->GetPostYScale()); |
|
1915 |
|
1916 nsIntRect visibleRect = data->mVisibleRegion.GetBounds(); |
|
1917 visibleRect.MoveBy(-GetTranslationForThebesLayer(data->mLayer)); |
|
1918 colorLayer->SetBounds(visibleRect); |
|
1919 |
|
1920 layer = colorLayer; |
|
1921 } |
|
1922 |
|
1923 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???"); |
|
1924 AutoLayersArray::index_type index = mNewChildLayers.IndexOf(data->mLayer); |
|
1925 NS_ASSERTION(index != AutoLayersArray::NoIndex, "Thebes layer not found?"); |
|
1926 mNewChildLayers.InsertElementAt(index + 1, layer); |
|
1927 |
|
1928 // Hide the ThebesLayer. We leave it in the layer tree so that we |
|
1929 // can find and recycle it later. |
|
1930 nsIntRect emptyRect; |
|
1931 data->mLayer->SetClipRect(&emptyRect); |
|
1932 data->mLayer->SetVisibleRegion(nsIntRegion()); |
|
1933 data->mLayer->SetEventRegions(EventRegions()); |
|
1934 } else { |
|
1935 layer = data->mLayer; |
|
1936 imageContainer = nullptr; |
|
1937 layer->SetClipRect(nullptr); |
|
1938 } |
|
1939 |
|
1940 Matrix transform; |
|
1941 if (!layer->GetTransform().Is2D(&transform)) { |
|
1942 NS_ERROR("Only 2D transformations currently supported"); |
|
1943 } |
|
1944 |
|
1945 // ImageLayers are already configured with a visible region |
|
1946 if (!imageContainer) { |
|
1947 NS_ASSERTION(!transform.HasNonIntegerTranslation(), |
|
1948 "Matrix not just an integer translation?"); |
|
1949 // Convert from relative to the container to relative to the |
|
1950 // ThebesLayer itself. |
|
1951 nsIntRegion rgn = data->mVisibleRegion; |
|
1952 rgn.MoveBy(-GetTranslationForThebesLayer(data->mLayer)); |
|
1953 layer->SetVisibleRegion(rgn); |
|
1954 } |
|
1955 |
|
1956 nsIntRegion transparentRegion; |
|
1957 transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion); |
|
1958 bool isOpaque = transparentRegion.IsEmpty(); |
|
1959 // For translucent ThebesLayers, try to find an opaque background |
|
1960 // color that covers the entire area beneath it so we can pull that |
|
1961 // color into this layer to make it opaque. |
|
1962 if (layer == data->mLayer) { |
|
1963 nscolor backgroundColor = NS_RGBA(0,0,0,0); |
|
1964 if (!isOpaque) { |
|
1965 backgroundColor = FindOpaqueBackgroundColorFor(lastIndex); |
|
1966 if (NS_GET_A(backgroundColor) == 255) { |
|
1967 isOpaque = true; |
|
1968 } |
|
1969 } |
|
1970 |
|
1971 // Store the background color |
|
1972 ThebesDisplayItemLayerUserData* userData = |
|
1973 GetThebesDisplayItemLayerUserData(data->mLayer); |
|
1974 NS_ASSERTION(userData, "where did our user data go?"); |
|
1975 if (userData->mForcedBackgroundColor != backgroundColor) { |
|
1976 // Invalidate the entire target ThebesLayer since we're changing |
|
1977 // the background color |
|
1978 data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion()); |
|
1979 } |
|
1980 userData->mForcedBackgroundColor = backgroundColor; |
|
1981 |
|
1982 // use a mask layer for rounded rect clipping. |
|
1983 // data->mCommonClipCount may be -1 if we haven't put any actual |
|
1984 // drawable items in this layer (i.e. it's only catching events). |
|
1985 int32_t commonClipCount = std::max(0, data->mCommonClipCount); |
|
1986 SetupMaskLayer(layer, data->mItemClip, commonClipCount); |
|
1987 // copy commonClipCount to the entry |
|
1988 FrameLayerBuilder::ThebesLayerItemsEntry* entry = mLayerBuilder-> |
|
1989 GetThebesLayerItemsEntry(static_cast<ThebesLayer*>(layer.get())); |
|
1990 entry->mCommonClipCount = commonClipCount; |
|
1991 } else { |
|
1992 // mask layer for image and color layers |
|
1993 SetupMaskLayer(layer, data->mItemClip); |
|
1994 } |
|
1995 |
|
1996 uint32_t flags = 0; |
|
1997 nsIWidget* widget = mContainerReferenceFrame->PresContext()->GetRootWidget(); |
|
1998 // See bug 941095. Not quite ready to disable this. |
|
1999 bool hidpi = false && widget && widget->GetDefaultScale().scale >= 2; |
|
2000 if (hidpi) { |
|
2001 flags |= Layer::CONTENT_DISABLE_SUBPIXEL_AA; |
|
2002 } |
|
2003 if (isOpaque && !data->mForceTransparentSurface) { |
|
2004 flags |= Layer::CONTENT_OPAQUE; |
|
2005 } else if (data->mNeedComponentAlpha && !hidpi) { |
|
2006 flags |= Layer::CONTENT_COMPONENT_ALPHA; |
|
2007 } |
|
2008 layer->SetContentFlags(flags); |
|
2009 |
|
2010 SetFixedPositionLayerData(layer, data->mFixedPosFrameForLayerData); |
|
2011 |
|
2012 ThebesLayerData* containingThebesLayerData = |
|
2013 mLayerBuilder->GetContainingThebesLayerData(); |
|
2014 if (containingThebesLayerData) { |
|
2015 gfx3DMatrix matrix = GetTransformToRoot(layer); |
|
2016 nsIntPoint translatedDest = GetTranslationForThebesLayer(containingThebesLayerData->mLayer); |
|
2017 matrix.TranslatePost(-gfxPoint3D(translatedDest.x, translatedDest.y, 0)); |
|
2018 AddTransformedBoundsToRegion(data->mDispatchToContentHitRegion, matrix, |
|
2019 &containingThebesLayerData->mDispatchToContentHitRegion); |
|
2020 AddTransformedBoundsToRegion(data->mMaybeHitRegion, matrix, |
|
2021 &containingThebesLayerData->mMaybeHitRegion); |
|
2022 // Our definitely-hit region must go to the maybe-hit-region since |
|
2023 // this function is an approximation. |
|
2024 gfxMatrix matrix2D; |
|
2025 bool isPrecise = matrix.Is2D(&matrix2D) && !matrix2D.HasNonAxisAlignedTransform(); |
|
2026 AddTransformedBoundsToRegion(data->mHitRegion, matrix, |
|
2027 isPrecise ? &containingThebesLayerData->mHitRegion |
|
2028 : &containingThebesLayerData->mMaybeHitRegion); |
|
2029 } else { |
|
2030 EventRegions regions; |
|
2031 regions.mHitRegion.Swap(&data->mHitRegion); |
|
2032 // Points whose hit-region status we're not sure about need to be dispatched |
|
2033 // to the content thread. |
|
2034 regions.mDispatchToContentHitRegion.Sub(data->mMaybeHitRegion, regions.mHitRegion); |
|
2035 regions.mDispatchToContentHitRegion.Or(regions.mDispatchToContentHitRegion, |
|
2036 data->mDispatchToContentHitRegion); |
|
2037 layer->SetEventRegions(regions); |
|
2038 } |
|
2039 |
|
2040 if (lastIndex > 0) { |
|
2041 // Since we're going to pop off the last ThebesLayerData, the |
|
2042 // mVisibleAboveRegion of the second-to-last item will need to include |
|
2043 // the regions of the last item. |
|
2044 ThebesLayerData* nextData = mThebesLayerDataStack[lastIndex - 1]; |
|
2045 nextData->CopyAboveRegion(data); |
|
2046 } |
|
2047 |
|
2048 mThebesLayerDataStack.RemoveElementAt(lastIndex); |
|
2049 } |
|
2050 |
|
2051 static bool |
|
2052 SuppressComponentAlpha(nsDisplayListBuilder* aBuilder, |
|
2053 nsDisplayItem* aItem, |
|
2054 const nsRect& aComponentAlphaBounds) |
|
2055 { |
|
2056 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); |
|
2057 if (!windowTransparentRegion || windowTransparentRegion->IsEmpty()) |
|
2058 return false; |
|
2059 |
|
2060 // Suppress component alpha for items in the toplevel window that are over |
|
2061 // the window translucent area |
|
2062 nsIFrame* f = aItem->Frame(); |
|
2063 nsIFrame* ref = aBuilder->RootReferenceFrame(); |
|
2064 if (f->PresContext() != ref->PresContext()) |
|
2065 return false; |
|
2066 |
|
2067 for (nsIFrame* t = f; t; t = t->GetParent()) { |
|
2068 if (t->IsTransformed()) |
|
2069 return false; |
|
2070 } |
|
2071 |
|
2072 return windowTransparentRegion->Intersects(aComponentAlphaBounds); |
|
2073 } |
|
2074 |
|
2075 static bool |
|
2076 WindowHasTransparency(nsDisplayListBuilder* aBuilder) |
|
2077 { |
|
2078 const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); |
|
2079 return windowTransparentRegion && !windowTransparentRegion->IsEmpty(); |
|
2080 } |
|
2081 |
|
2082 void |
|
2083 ThebesLayerData::Accumulate(ContainerState* aState, |
|
2084 nsDisplayItem* aItem, |
|
2085 const nsIntRect& aVisibleRect, |
|
2086 const nsIntRect& aDrawRect, |
|
2087 const DisplayItemClip& aClip) |
|
2088 { |
|
2089 if (aState->mBuilder->NeedToForceTransparentSurfaceForItem(aItem)) { |
|
2090 mForceTransparentSurface = true; |
|
2091 } |
|
2092 if (aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { |
|
2093 // Disable component alpha. |
|
2094 // Note that the transform (if any) on the ThebesLayer is always an integer translation so |
|
2095 // we don't have to factor that in here. |
|
2096 aItem->DisableComponentAlpha(); |
|
2097 } |
|
2098 |
|
2099 /* Mark as available for conversion to image layer if this is a nsDisplayImage and |
|
2100 * we are the first visible item in the ThebesLayerData object. |
|
2101 */ |
|
2102 if (mVisibleRegion.IsEmpty() && |
|
2103 aItem->SupportsOptimizingToImage()) { |
|
2104 mImage = static_cast<nsDisplayImageContainer*>(aItem); |
|
2105 } else { |
|
2106 mImage = nullptr; |
|
2107 } |
|
2108 bool clipMatches = mItemClip == aClip; |
|
2109 mItemClip = aClip; |
|
2110 |
|
2111 if (!mIsSolidColorInVisibleRegion && mOpaqueRegion.Contains(aDrawRect) && |
|
2112 mVisibleRegion.Contains(aVisibleRect)) { |
|
2113 // A very common case! Most pages have a ThebesLayer with the page |
|
2114 // background (opaque) visible and most or all of the page content over the |
|
2115 // top of that background. |
|
2116 // The rest of this method won't do anything. mVisibleRegion, mOpaqueRegion |
|
2117 // and mDrawRegion don't need updating. mVisibleRegion contains aVisibleRect |
|
2118 // already, mOpaqueRegion contains aDrawRect and therefore whatever |
|
2119 // the opaque region of the item is. mDrawRegion must contain mOpaqueRegion |
|
2120 // and therefore aDrawRect. |
|
2121 NS_ASSERTION(mDrawRegion.Contains(aDrawRect), "Draw region not covered"); |
|
2122 return; |
|
2123 } |
|
2124 |
|
2125 nscolor uniformColor; |
|
2126 bool isUniform = aItem->IsUniform(aState->mBuilder, &uniformColor); |
|
2127 |
|
2128 // Some display items have to exist (so they can set forceTransparentSurface |
|
2129 // below) but don't draw anything. They'll return true for isUniform but |
|
2130 // a color with opacity 0. |
|
2131 if (!isUniform || NS_GET_A(uniformColor) > 0) { |
|
2132 // Make sure that the visible area is covered by uniform pixels. In |
|
2133 // particular this excludes cases where the edges of the item are not |
|
2134 // pixel-aligned (thus the item will not be truly uniform). |
|
2135 if (isUniform) { |
|
2136 bool snap; |
|
2137 nsRect bounds = aItem->GetBounds(aState->mBuilder, &snap); |
|
2138 if (!aState->ScaleToInsidePixels(bounds, snap).Contains(aVisibleRect)) { |
|
2139 isUniform = false; |
|
2140 } |
|
2141 } |
|
2142 if (isUniform) { |
|
2143 if (mVisibleRegion.IsEmpty()) { |
|
2144 // This color is all we have |
|
2145 mSolidColor = uniformColor; |
|
2146 mIsSolidColorInVisibleRegion = true; |
|
2147 } else if (mIsSolidColorInVisibleRegion && |
|
2148 mVisibleRegion.IsEqual(nsIntRegion(aVisibleRect)) && |
|
2149 clipMatches) { |
|
2150 // we can just blend the colors together |
|
2151 mSolidColor = NS_ComposeColors(mSolidColor, uniformColor); |
|
2152 } else { |
|
2153 mIsSolidColorInVisibleRegion = false; |
|
2154 } |
|
2155 } else { |
|
2156 mIsSolidColorInVisibleRegion = false; |
|
2157 } |
|
2158 |
|
2159 mVisibleRegion.Or(mVisibleRegion, aVisibleRect); |
|
2160 mVisibleRegion.SimplifyOutward(4); |
|
2161 mDrawRegion.Or(mDrawRegion, aDrawRect); |
|
2162 mDrawRegion.SimplifyOutward(4); |
|
2163 } |
|
2164 |
|
2165 bool snap; |
|
2166 nsRegion opaque = aItem->GetOpaqueRegion(aState->mBuilder, &snap); |
|
2167 if (!opaque.IsEmpty()) { |
|
2168 nsRegion opaqueClipped; |
|
2169 nsRegionRectIterator iter(opaque); |
|
2170 for (const nsRect* r = iter.Next(); r; r = iter.Next()) { |
|
2171 opaqueClipped.Or(opaqueClipped, aClip.ApproximateIntersectInward(*r)); |
|
2172 } |
|
2173 |
|
2174 nsIntRegion opaquePixels = aState->ScaleRegionToInsidePixels(opaqueClipped, snap); |
|
2175 |
|
2176 nsIntRegionRectIterator iter2(opaquePixels); |
|
2177 for (const nsIntRect* r = iter2.Next(); r; r = iter2.Next()) { |
|
2178 // We don't use SimplifyInward here since it's not defined exactly |
|
2179 // what it will discard. For our purposes the most important case |
|
2180 // is a large opaque background at the bottom of z-order (e.g., |
|
2181 // a canvas background), so we need to make sure that the first rect |
|
2182 // we see doesn't get discarded. |
|
2183 nsIntRegion tmp; |
|
2184 tmp.Or(mOpaqueRegion, *r); |
|
2185 // Opaque display items in chrome documents whose window is partially |
|
2186 // transparent are always added to the opaque region. This helps ensure |
|
2187 // that we get as much subpixel-AA as possible in the chrome. |
|
2188 if (tmp.GetNumRects() <= 4 || |
|
2189 (WindowHasTransparency(aState->mBuilder) && |
|
2190 aItem->Frame()->PresContext()->IsChrome())) { |
|
2191 mOpaqueRegion = tmp; |
|
2192 } |
|
2193 } |
|
2194 } |
|
2195 |
|
2196 if (!aState->mParameters.mDisableSubpixelAntialiasingInDescendants) { |
|
2197 nsRect componentAlpha = aItem->GetComponentAlphaBounds(aState->mBuilder); |
|
2198 if (!componentAlpha.IsEmpty()) { |
|
2199 nsIntRect componentAlphaRect = |
|
2200 aState->ScaleToOutsidePixels(componentAlpha, false).Intersect(aVisibleRect); |
|
2201 if (!mOpaqueRegion.Contains(componentAlphaRect)) { |
|
2202 if (SuppressComponentAlpha(aState->mBuilder, aItem, componentAlpha)) { |
|
2203 aItem->DisableComponentAlpha(); |
|
2204 } else { |
|
2205 mNeedComponentAlpha = true; |
|
2206 } |
|
2207 } |
|
2208 } |
|
2209 } |
|
2210 } |
|
2211 |
|
2212 ThebesLayerData* |
|
2213 ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, |
|
2214 const nsIntRect& aVisibleRect, |
|
2215 const nsIFrame* aAnimatedGeometryRoot, |
|
2216 const nsPoint& aTopLeft, |
|
2217 bool aShouldFixToViewport) |
|
2218 { |
|
2219 int32_t i; |
|
2220 int32_t lowestUsableLayerWithScrolledRoot = -1; |
|
2221 int32_t topmostLayerWithScrolledRoot = -1; |
|
2222 for (i = mThebesLayerDataStack.Length() - 1; i >= 0; --i) { |
|
2223 // Don't let should-fix-to-viewport items share a layer with any other items. |
|
2224 if (aShouldFixToViewport) { |
|
2225 ++i; |
|
2226 break; |
|
2227 } |
|
2228 ThebesLayerData* data = mThebesLayerDataStack[i]; |
|
2229 // Give up if there is content drawn above (in z-order) this layer that |
|
2230 // intersects aItem's visible region; aItem must be placed in a |
|
2231 // layer this layer. |
|
2232 if (data->DrawAboveRegionIntersects(aVisibleRect)) { |
|
2233 ++i; |
|
2234 break; |
|
2235 } |
|
2236 // If the animated scrolled roots are the same and we can share this layer |
|
2237 // with the item, note this as a usable layer. |
|
2238 if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot && |
|
2239 !data->mSingleItemFixedToViewport) { |
|
2240 lowestUsableLayerWithScrolledRoot = i; |
|
2241 if (topmostLayerWithScrolledRoot < 0) { |
|
2242 topmostLayerWithScrolledRoot = i; |
|
2243 } |
|
2244 } |
|
2245 // If the layer's drawn region intersects the item, stop now since no |
|
2246 // lower layer will be usable. Do the same if the layer is subject to |
|
2247 // async transforms, since we don't know where it will really be drawn. |
|
2248 if (data->DrawRegionIntersects(aVisibleRect)) |
|
2249 break; |
|
2250 } |
|
2251 if (topmostLayerWithScrolledRoot < 0) { |
|
2252 --i; |
|
2253 for (; i >= 0; --i) { |
|
2254 ThebesLayerData* data = mThebesLayerDataStack[i]; |
|
2255 if (data->mAnimatedGeometryRoot == aAnimatedGeometryRoot) { |
|
2256 topmostLayerWithScrolledRoot = i; |
|
2257 break; |
|
2258 } |
|
2259 } |
|
2260 } |
|
2261 |
|
2262 if (topmostLayerWithScrolledRoot >= 0) { |
|
2263 while (uint32_t(topmostLayerWithScrolledRoot + 1) < mThebesLayerDataStack.Length()) { |
|
2264 PopThebesLayerData(); |
|
2265 } |
|
2266 } |
|
2267 |
|
2268 ThebesLayerData* thebesLayerData = nullptr; |
|
2269 if (lowestUsableLayerWithScrolledRoot < 0) { |
|
2270 nsRefPtr<ThebesLayer> layer = |
|
2271 CreateOrRecycleThebesLayer(aAnimatedGeometryRoot, aItem->ReferenceFrame(), aTopLeft); |
|
2272 |
|
2273 thebesLayerData = new ThebesLayerData(); |
|
2274 mThebesLayerDataStack.AppendElement(thebesLayerData); |
|
2275 thebesLayerData->mLayer = layer; |
|
2276 thebesLayerData->mAnimatedGeometryRoot = aAnimatedGeometryRoot; |
|
2277 thebesLayerData->mFixedPosFrameForLayerData = |
|
2278 FindFixedPosFrameForLayerData(aAnimatedGeometryRoot, aShouldFixToViewport); |
|
2279 thebesLayerData->mReferenceFrame = aItem->ReferenceFrame(); |
|
2280 thebesLayerData->mSingleItemFixedToViewport = aShouldFixToViewport; |
|
2281 |
|
2282 NS_ASSERTION(!mNewChildLayers.Contains(layer), "Layer already in list???"); |
|
2283 *mNewChildLayers.AppendElement() = layer.forget(); |
|
2284 } else { |
|
2285 thebesLayerData = mThebesLayerDataStack[lowestUsableLayerWithScrolledRoot]; |
|
2286 } |
|
2287 |
|
2288 return thebesLayerData; |
|
2289 } |
|
2290 |
|
2291 #ifdef MOZ_DUMP_PAINTING |
|
2292 static void |
|
2293 DumpPaintedImage(nsDisplayItem* aItem, gfxASurface* aSurf) |
|
2294 { |
|
2295 nsCString string(aItem->Name()); |
|
2296 string.Append("-"); |
|
2297 string.AppendInt((uint64_t)aItem); |
|
2298 fprintf_stderr(gfxUtils::sDumpPaintFile, "array[\"%s\"]=\"", string.BeginReading()); |
|
2299 aSurf->DumpAsDataURL(gfxUtils::sDumpPaintFile); |
|
2300 fprintf_stderr(gfxUtils::sDumpPaintFile, "\";"); |
|
2301 } |
|
2302 #endif |
|
2303 |
|
2304 static void |
|
2305 PaintInactiveLayer(nsDisplayListBuilder* aBuilder, |
|
2306 LayerManager* aManager, |
|
2307 nsDisplayItem* aItem, |
|
2308 gfxContext* aContext, |
|
2309 nsRenderingContext* aCtx) |
|
2310 { |
|
2311 // This item has an inactive layer. Render it to a ThebesLayer |
|
2312 // using a temporary BasicLayerManager. |
|
2313 BasicLayerManager* basic = static_cast<BasicLayerManager*>(aManager); |
|
2314 nsRefPtr<gfxContext> context = aContext; |
|
2315 #ifdef MOZ_DUMP_PAINTING |
|
2316 int32_t appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem); |
|
2317 nsIntRect itemVisibleRect = |
|
2318 aItem->GetVisibleRect().ToOutsidePixels(appUnitsPerDevPixel); |
|
2319 |
|
2320 nsRefPtr<gfxASurface> surf; |
|
2321 if (gfxUtils::sDumpPainting) { |
|
2322 surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(itemVisibleRect.Size().ToIntSize(), |
|
2323 gfxContentType::COLOR_ALPHA); |
|
2324 surf->SetDeviceOffset(-itemVisibleRect.TopLeft()); |
|
2325 context = new gfxContext(surf); |
|
2326 } |
|
2327 #endif |
|
2328 basic->BeginTransaction(); |
|
2329 basic->SetTarget(context); |
|
2330 |
|
2331 if (aItem->GetType() == nsDisplayItem::TYPE_SVG_EFFECTS) { |
|
2332 static_cast<nsDisplaySVGEffects*>(aItem)->PaintAsLayer(aBuilder, aCtx, basic); |
|
2333 if (basic->InTransaction()) { |
|
2334 basic->AbortTransaction(); |
|
2335 } |
|
2336 } else { |
|
2337 basic->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder); |
|
2338 } |
|
2339 FrameLayerBuilder *builder = static_cast<FrameLayerBuilder*>(basic->GetUserData(&gLayerManagerLayerBuilder)); |
|
2340 if (builder) { |
|
2341 builder->DidEndTransaction(); |
|
2342 } |
|
2343 |
|
2344 basic->SetTarget(nullptr); |
|
2345 |
|
2346 #ifdef MOZ_DUMP_PAINTING |
|
2347 if (gfxUtils::sDumpPainting) { |
|
2348 DumpPaintedImage(aItem, surf); |
|
2349 |
|
2350 surf->SetDeviceOffset(gfxPoint(0, 0)); |
|
2351 aContext->SetSource(surf, itemVisibleRect.TopLeft()); |
|
2352 aContext->Rectangle(itemVisibleRect); |
|
2353 aContext->Fill(); |
|
2354 aItem->SetPainted(); |
|
2355 } |
|
2356 #endif |
|
2357 } |
|
2358 |
|
2359 /** |
|
2360 * Chooses a single active scrolled root for the entire display list, used |
|
2361 * when we are flattening layers. |
|
2362 */ |
|
2363 bool |
|
2364 ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, |
|
2365 const nsIFrame **aAnimatedGeometryRoot) |
|
2366 { |
|
2367 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { |
|
2368 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); |
|
2369 // Don't use an item that won't be part of any ThebesLayers to pick the |
|
2370 // active scrolled root. |
|
2371 if (layerState == LAYER_ACTIVE_FORCE) { |
|
2372 continue; |
|
2373 } |
|
2374 |
|
2375 // Try using the actual active scrolled root of the backmost item, as that |
|
2376 // should result in the least invalidation when scrolling. |
|
2377 *aAnimatedGeometryRoot = |
|
2378 nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); |
|
2379 return true; |
|
2380 } |
|
2381 return false; |
|
2382 } |
|
2383 |
|
2384 /* |
|
2385 * Iterate through the non-clip items in aList and its descendants. |
|
2386 * For each item we compute the effective clip rect. Each item is assigned |
|
2387 * to a layer. We invalidate the areas in ThebesLayers where an item |
|
2388 * has moved from one ThebesLayer to another. Also, |
|
2389 * aState->mInvalidThebesContent is invalidated in every ThebesLayer. |
|
2390 * We set the clip rect for items that generated their own layer, and |
|
2391 * create a mask layer to do any rounded rect clipping. |
|
2392 * (ThebesLayers don't need a clip rect on the layer, we clip the items |
|
2393 * individually when we draw them.) |
|
2394 * We set the visible rect for all layers, although the actual setting |
|
2395 * of visible rects for some ThebesLayers is deferred until the calling |
|
2396 * of ContainerState::Finish. |
|
2397 */ |
|
2398 void |
|
2399 ContainerState::ProcessDisplayItems(const nsDisplayList& aList, |
|
2400 uint32_t aFlags) |
|
2401 { |
|
2402 PROFILER_LABEL("ContainerState", "ProcessDisplayItems"); |
|
2403 |
|
2404 const nsIFrame* lastAnimatedGeometryRoot = mContainerReferenceFrame; |
|
2405 nsPoint topLeft(0,0); |
|
2406 |
|
2407 // When NO_COMPONENT_ALPHA is set, items will be flattened into a single |
|
2408 // layer, so we need to choose which active scrolled root to use for all |
|
2409 // items. |
|
2410 if (aFlags & NO_COMPONENT_ALPHA) { |
|
2411 if (ChooseAnimatedGeometryRoot(aList, &lastAnimatedGeometryRoot)) { |
|
2412 topLeft = lastAnimatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame); |
|
2413 } |
|
2414 } |
|
2415 |
|
2416 int32_t maxLayers = nsDisplayItem::MaxActiveLayers(); |
|
2417 int layerCount = 0; |
|
2418 |
|
2419 for (nsDisplayItem* item = aList.GetBottom(); item; item = item->GetAbove()) { |
|
2420 NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item), |
|
2421 "items in a container layer should all have the same app units per dev pixel"); |
|
2422 |
|
2423 nsIntRect itemVisibleRect = |
|
2424 ScaleToOutsidePixels(item->GetVisibleRect(), false); |
|
2425 bool snap; |
|
2426 nsRect itemContent = item->GetBounds(mBuilder, &snap); |
|
2427 nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap); |
|
2428 nsDisplayItem::Type itemType = item->GetType(); |
|
2429 nsIntRect clipRect; |
|
2430 const DisplayItemClip& itemClip = item->GetClip(); |
|
2431 if (itemClip.HasClip()) { |
|
2432 itemContent.IntersectRect(itemContent, itemClip.GetClipRect()); |
|
2433 clipRect = ScaleToNearestPixels(itemClip.GetClipRect()); |
|
2434 itemDrawRect.IntersectRect(itemDrawRect, clipRect); |
|
2435 clipRect.MoveBy(mParameters.mOffset); |
|
2436 } |
|
2437 mBounds.UnionRect(mBounds, itemContent); |
|
2438 itemVisibleRect.IntersectRect(itemVisibleRect, itemDrawRect); |
|
2439 |
|
2440 LayerState layerState = item->GetLayerState(mBuilder, mManager, mParameters); |
|
2441 if (layerState == LAYER_INACTIVE && |
|
2442 nsDisplayItem::ForceActiveLayers()) { |
|
2443 layerState = LAYER_ACTIVE; |
|
2444 } |
|
2445 |
|
2446 bool forceInactive; |
|
2447 const nsIFrame* animatedGeometryRoot; |
|
2448 if (aFlags & NO_COMPONENT_ALPHA) { |
|
2449 forceInactive = true; |
|
2450 animatedGeometryRoot = lastAnimatedGeometryRoot; |
|
2451 } else { |
|
2452 forceInactive = false; |
|
2453 if (mManager->IsWidgetLayerManager()) { |
|
2454 animatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); |
|
2455 } else { |
|
2456 // For inactive layer subtrees, splitting content into ThebesLayers |
|
2457 // based on animated geometry roots is pointless. It's more efficient |
|
2458 // to build the minimum number of layers. |
|
2459 animatedGeometryRoot = mContainerAnimatedGeometryRoot; |
|
2460 } |
|
2461 if (animatedGeometryRoot != lastAnimatedGeometryRoot) { |
|
2462 lastAnimatedGeometryRoot = animatedGeometryRoot; |
|
2463 topLeft = animatedGeometryRoot->GetOffsetToCrossDoc(mContainerReferenceFrame); |
|
2464 } |
|
2465 } |
|
2466 bool shouldFixToViewport = !animatedGeometryRoot->GetParent() && |
|
2467 item->ShouldFixToViewport(mBuilder); |
|
2468 |
|
2469 if (maxLayers != -1 && layerCount >= maxLayers) { |
|
2470 forceInactive = true; |
|
2471 } |
|
2472 |
|
2473 // Assign the item to a layer |
|
2474 if (layerState == LAYER_ACTIVE_FORCE || |
|
2475 (layerState == LAYER_INACTIVE && !mManager->IsWidgetLayerManager()) || |
|
2476 (!forceInactive && |
|
2477 (layerState == LAYER_ACTIVE_EMPTY || |
|
2478 layerState == LAYER_ACTIVE))) { |
|
2479 |
|
2480 layerCount++; |
|
2481 |
|
2482 // LAYER_ACTIVE_EMPTY means the layer is created just for its metadata. |
|
2483 // We should never see an empty layer with any visible content! |
|
2484 NS_ASSERTION(layerState != LAYER_ACTIVE_EMPTY || |
|
2485 itemVisibleRect.IsEmpty(), |
|
2486 "State is LAYER_ACTIVE_EMPTY but visible rect is not."); |
|
2487 |
|
2488 // As long as the new layer isn't going to be a ThebesLayer, |
|
2489 // InvalidateForLayerChange doesn't need the new layer pointer. |
|
2490 // We also need to check the old data now, because BuildLayer |
|
2491 // can overwrite it. |
|
2492 InvalidateForLayerChange(item, nullptr, itemClip, topLeft, nullptr); |
|
2493 |
|
2494 // If the item would have its own layer but is invisible, just hide it. |
|
2495 // Note that items without their own layers can't be skipped this |
|
2496 // way, since their ThebesLayer may decide it wants to draw them |
|
2497 // into its buffer even if they're currently covered. |
|
2498 if (itemVisibleRect.IsEmpty() && |
|
2499 !item->ShouldBuildLayerEvenIfInvisible(mBuilder)) { |
|
2500 continue; |
|
2501 } |
|
2502 |
|
2503 if (itemType == nsDisplayItem::TYPE_TRANSFORM) { |
|
2504 mParameters.mAncestorClipRect = itemClip.HasClip() ? &clipRect : nullptr; |
|
2505 } else { |
|
2506 mParameters.mAncestorClipRect = nullptr; |
|
2507 } |
|
2508 |
|
2509 // Just use its layer. |
|
2510 nsRefPtr<Layer> ownLayer = item->BuildLayer(mBuilder, mManager, mParameters); |
|
2511 if (!ownLayer) { |
|
2512 continue; |
|
2513 } |
|
2514 |
|
2515 NS_ASSERTION(!ownLayer->AsThebesLayer(), |
|
2516 "Should never have created a dedicated Thebes layer!"); |
|
2517 |
|
2518 const nsIFrame* fixedPosFrame = |
|
2519 FindFixedPosFrameForLayerData(animatedGeometryRoot, shouldFixToViewport); |
|
2520 if (fixedPosFrame) { |
|
2521 nsIntRegion visibleRegion(itemVisibleRect); |
|
2522 AdjustLayerDataForFixedPositioning(fixedPosFrame, |
|
2523 nsIntRegion(itemDrawRect), &visibleRegion); |
|
2524 itemVisibleRect = visibleRegion.GetBounds(); |
|
2525 } |
|
2526 SetFixedPositionLayerData(ownLayer, fixedPosFrame); |
|
2527 |
|
2528 nsRect invalid; |
|
2529 if (item->IsInvalid(invalid)) { |
|
2530 ownLayer->SetInvalidRectToVisibleRegion(); |
|
2531 } |
|
2532 |
|
2533 // If it's not a ContainerLayer, we need to apply the scale transform |
|
2534 // ourselves. |
|
2535 if (!ownLayer->AsContainerLayer()) { |
|
2536 ownLayer->SetPostScale(mParameters.mXScale, |
|
2537 mParameters.mYScale); |
|
2538 } |
|
2539 |
|
2540 // Update that layer's clip and visible rects. |
|
2541 NS_ASSERTION(ownLayer->Manager() == mManager, "Wrong manager"); |
|
2542 NS_ASSERTION(!ownLayer->HasUserData(&gLayerManagerUserData), |
|
2543 "We shouldn't have a FrameLayerBuilder-managed layer here!"); |
|
2544 NS_ASSERTION(itemClip.HasClip() || |
|
2545 itemClip.GetRoundedRectCount() == 0, |
|
2546 "If we have rounded rects, we must have a clip rect"); |
|
2547 // It has its own layer. Update that layer's clip and visible rects. |
|
2548 if (itemClip.HasClip()) { |
|
2549 ownLayer->SetClipRect(&clipRect); |
|
2550 } else { |
|
2551 ownLayer->SetClipRect(nullptr); |
|
2552 } |
|
2553 ThebesLayerData* data = GetTopThebesLayerData(); |
|
2554 if (data) { |
|
2555 // Prerendered transform items can be updated without layer building |
|
2556 // (async animations or an empty transaction), so we treat all other |
|
2557 // content as being above this so that the transformed layer can correctly |
|
2558 // move behind other content. |
|
2559 if (item->GetType() == nsDisplayItem::TYPE_TRANSFORM && |
|
2560 nsDisplayTransform::ShouldPrerenderTransformedContent(mBuilder, |
|
2561 item->Frame(), |
|
2562 false)) { |
|
2563 data->SetAllDrawingAbove(); |
|
2564 } else { |
|
2565 data->AddVisibleAboveRegion(itemVisibleRect); |
|
2566 |
|
2567 // Add the entire bounds rect to the mDrawAboveRegion. |
|
2568 // The visible region may be excluding opaque content above the |
|
2569 // item, and we need to ensure that that content is not placed |
|
2570 // in a ThebesLayer below the item! |
|
2571 data->AddDrawAboveRegion(itemDrawRect); |
|
2572 } |
|
2573 } |
|
2574 itemVisibleRect.MoveBy(mParameters.mOffset); |
|
2575 if (item->SetVisibleRegionOnLayer()) { |
|
2576 SetVisibleRegionForLayer(ownLayer, ownLayer->GetVisibleRegion(), itemVisibleRect); |
|
2577 } |
|
2578 |
|
2579 // rounded rectangle clipping using mask layers |
|
2580 // (must be done after visible rect is set on layer) |
|
2581 if (itemClip.IsRectClippedByRoundedCorner(itemContent)) { |
|
2582 SetupMaskLayer(ownLayer, itemClip); |
|
2583 } |
|
2584 |
|
2585 ContainerLayer* oldContainer = ownLayer->GetParent(); |
|
2586 if (oldContainer && oldContainer != mContainerLayer) { |
|
2587 oldContainer->RemoveChild(ownLayer); |
|
2588 } |
|
2589 NS_ASSERTION(!mNewChildLayers.Contains(ownLayer), |
|
2590 "Layer already in list???"); |
|
2591 |
|
2592 mNewChildLayers.AppendElement(ownLayer); |
|
2593 |
|
2594 /** |
|
2595 * No need to allocate geometry for items that aren't |
|
2596 * part of a ThebesLayer. |
|
2597 */ |
|
2598 nsAutoPtr<nsDisplayItemGeometry> dummy; |
|
2599 mLayerBuilder->AddLayerDisplayItem(ownLayer, item, |
|
2600 itemClip, layerState, |
|
2601 topLeft, nullptr, |
|
2602 dummy); |
|
2603 } else { |
|
2604 ThebesLayerData* data = |
|
2605 FindThebesLayerFor(item, itemVisibleRect, animatedGeometryRoot, topLeft, |
|
2606 shouldFixToViewport); |
|
2607 |
|
2608 if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) { |
|
2609 nsDisplayLayerEventRegions* eventRegions = |
|
2610 static_cast<nsDisplayLayerEventRegions*>(item); |
|
2611 data->AccumulateEventRegions(ScaleRegionToOutsidePixels(eventRegions->HitRegion()), |
|
2612 ScaleRegionToOutsidePixels(eventRegions->MaybeHitRegion()), |
|
2613 ScaleRegionToOutsidePixels(eventRegions->DispatchToContentHitRegion())); |
|
2614 } else { |
|
2615 // check to see if the new item has rounded rect clips in common with |
|
2616 // other items in the layer |
|
2617 data->UpdateCommonClipCount(itemClip); |
|
2618 data->Accumulate(this, item, itemVisibleRect, itemDrawRect, itemClip); |
|
2619 |
|
2620 nsAutoPtr<nsDisplayItemGeometry> geometry(item->AllocateGeometry(mBuilder)); |
|
2621 InvalidateForLayerChange(item, data->mLayer, itemClip, topLeft, geometry); |
|
2622 |
|
2623 mLayerBuilder->AddThebesDisplayItem(data, item, itemClip, |
|
2624 mContainerFrame, |
|
2625 layerState, topLeft, |
|
2626 geometry); |
|
2627 } |
|
2628 } |
|
2629 } |
|
2630 } |
|
2631 |
|
2632 void |
|
2633 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, |
|
2634 Layer* aNewLayer, |
|
2635 const DisplayItemClip& aClip, |
|
2636 const nsPoint& aTopLeft, |
|
2637 nsDisplayItemGeometry *aGeometry) |
|
2638 { |
|
2639 NS_ASSERTION(aItem->GetPerFrameKey(), |
|
2640 "Display items that render using Thebes must have a key"); |
|
2641 nsDisplayItemGeometry *oldGeometry = nullptr; |
|
2642 DisplayItemClip* oldClip = nullptr; |
|
2643 nsAutoTArray<nsIFrame*,4> changedFrames; |
|
2644 bool isInvalid = false; |
|
2645 Layer* oldLayer = mLayerBuilder->GetOldLayerFor(aItem, &oldGeometry, &oldClip, &changedFrames, &isInvalid); |
|
2646 if (aNewLayer != oldLayer && oldLayer) { |
|
2647 // The item has changed layers. |
|
2648 // Invalidate the old bounds in the old layer and new bounds in the new layer. |
|
2649 ThebesLayer* t = oldLayer->AsThebesLayer(); |
|
2650 if (t) { |
|
2651 // Note that whenever the layer's scale changes, we invalidate the whole thing, |
|
2652 // so it doesn't matter whether we are using the old scale at last paint |
|
2653 // or a new scale here |
|
2654 #ifdef MOZ_DUMP_PAINTING |
|
2655 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
2656 printf_stderr("Display item type %s(%p) changed layers %p to %p!\n", aItem->Name(), aItem->Frame(), t, aNewLayer); |
|
2657 } |
|
2658 #endif |
|
2659 InvalidatePostTransformRegion(t, |
|
2660 oldGeometry->ComputeInvalidationRegion(), |
|
2661 *oldClip, |
|
2662 mLayerBuilder->GetLastPaintOffset(t)); |
|
2663 } |
|
2664 if (aNewLayer) { |
|
2665 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer(); |
|
2666 if (newThebesLayer) { |
|
2667 InvalidatePostTransformRegion(newThebesLayer, |
|
2668 aGeometry->ComputeInvalidationRegion(), |
|
2669 aClip, |
|
2670 GetTranslationForThebesLayer(newThebesLayer)); |
|
2671 } |
|
2672 } |
|
2673 aItem->NotifyRenderingChanged(); |
|
2674 return; |
|
2675 } |
|
2676 if (!aNewLayer) { |
|
2677 return; |
|
2678 } |
|
2679 |
|
2680 ThebesLayer* newThebesLayer = aNewLayer->AsThebesLayer(); |
|
2681 if (!newThebesLayer) { |
|
2682 return; |
|
2683 } |
|
2684 |
|
2685 ThebesDisplayItemLayerUserData* data = |
|
2686 static_cast<ThebesDisplayItemLayerUserData*>(newThebesLayer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
2687 // If the frame is marked as invalidated, and didn't specify a rect to invalidate then we want to |
|
2688 // invalidate both the old and new bounds, otherwise we only want to invalidate the changed areas. |
|
2689 // If we do get an invalid rect, then we want to add this on top of the change areas. |
|
2690 nsRect invalid; |
|
2691 nsRegion combined; |
|
2692 nsPoint shift = aTopLeft - data->mLastAnimatedGeometryRootOrigin; |
|
2693 bool notifyRenderingChanged = true; |
|
2694 if (!oldLayer) { |
|
2695 // This item is being added for the first time, invalidate its entire area. |
|
2696 //TODO: We call GetGeometry again in AddThebesDisplayItem, we should reuse this. |
|
2697 combined = aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion()); |
|
2698 #ifdef MOZ_DUMP_PAINTING |
|
2699 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
2700 printf_stderr("Display item type %s(%p) added to layer %p!\n", aItem->Name(), aItem->Frame(), aNewLayer); |
|
2701 } |
|
2702 #endif |
|
2703 } else if (isInvalid || (aItem->IsInvalid(invalid) && invalid.IsEmpty())) { |
|
2704 // Either layout marked item as needing repainting, invalidate the entire old and new areas. |
|
2705 combined = oldClip->ApplyNonRoundedIntersection(oldGeometry->ComputeInvalidationRegion()); |
|
2706 combined.MoveBy(shift); |
|
2707 combined.Or(combined, aClip.ApplyNonRoundedIntersection(aGeometry->ComputeInvalidationRegion())); |
|
2708 #ifdef MOZ_DUMP_PAINTING |
|
2709 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
2710 printf_stderr("Display item type %s(%p) (in layer %p) belongs to an invalidated frame!\n", aItem->Name(), aItem->Frame(), aNewLayer); |
|
2711 } |
|
2712 #endif |
|
2713 } else { |
|
2714 // Let the display item check for geometry changes and decide what needs to be |
|
2715 // repainted. |
|
2716 |
|
2717 // We have an optimization to cache the drawing background-attachment: fixed canvas |
|
2718 // background images so we can scroll and just blit them when they are flattened into |
|
2719 // the same layer as scrolling content. NotifyRenderingChanged is only used to tell |
|
2720 // the canvas bg image item to purge this cache. We want to be careful not to accidentally |
|
2721 // purge the cache if we are just invalidating due to scrolling (ie the background image |
|
2722 // moves on the scrolling layer but it's rendering stays the same) so if |
|
2723 // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the |
|
2724 // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls |
|
2725 // NotifyRenderingChanged if anything changes). |
|
2726 if (oldGeometry->ComputeInvalidationRegion() == aGeometry->ComputeInvalidationRegion() && |
|
2727 *oldClip == aClip && invalid.IsEmpty() && changedFrames.Length() == 0) { |
|
2728 notifyRenderingChanged = false; |
|
2729 } |
|
2730 |
|
2731 oldGeometry->MoveBy(shift); |
|
2732 aItem->ComputeInvalidationRegion(mBuilder, oldGeometry, &combined); |
|
2733 oldClip->AddOffsetAndComputeDifference(shift, oldGeometry->ComputeInvalidationRegion(), |
|
2734 aClip, aGeometry->ComputeInvalidationRegion(), |
|
2735 &combined); |
|
2736 |
|
2737 // Add in any rect that the frame specified |
|
2738 combined.Or(combined, invalid); |
|
2739 |
|
2740 for (uint32_t i = 0; i < changedFrames.Length(); i++) { |
|
2741 combined.Or(combined, changedFrames[i]->GetVisualOverflowRect()); |
|
2742 } |
|
2743 |
|
2744 // Restrict invalidation to the clipped region |
|
2745 nsRegion clip; |
|
2746 if (aClip.ComputeRegionInClips(oldClip, shift, &clip)) { |
|
2747 combined.And(combined, clip); |
|
2748 } |
|
2749 #ifdef MOZ_DUMP_PAINTING |
|
2750 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
2751 if (!combined.IsEmpty()) { |
|
2752 printf_stderr("Display item type %s(%p) (in layer %p) changed geometry!\n", aItem->Name(), aItem->Frame(), aNewLayer); |
|
2753 } |
|
2754 } |
|
2755 #endif |
|
2756 } |
|
2757 if (!combined.IsEmpty()) { |
|
2758 if (notifyRenderingChanged) { |
|
2759 aItem->NotifyRenderingChanged(); |
|
2760 } |
|
2761 InvalidatePostTransformRegion(newThebesLayer, |
|
2762 combined.ScaleToOutsidePixels(data->mXScale, data->mYScale, mAppUnitsPerDevPixel), |
|
2763 GetTranslationForThebesLayer(newThebesLayer)); |
|
2764 } |
|
2765 } |
|
2766 |
|
2767 void |
|
2768 FrameLayerBuilder::AddThebesDisplayItem(ThebesLayerData* aLayerData, |
|
2769 nsDisplayItem* aItem, |
|
2770 const DisplayItemClip& aClip, |
|
2771 nsIFrame* aContainerLayerFrame, |
|
2772 LayerState aLayerState, |
|
2773 const nsPoint& aTopLeft, |
|
2774 nsAutoPtr<nsDisplayItemGeometry> aGeometry) |
|
2775 { |
|
2776 ThebesLayer* layer = aLayerData->mLayer; |
|
2777 ThebesDisplayItemLayerUserData* thebesData = |
|
2778 static_cast<ThebesDisplayItemLayerUserData*> |
|
2779 (layer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
2780 nsRefPtr<BasicLayerManager> tempManager; |
|
2781 nsIntRect intClip; |
|
2782 bool hasClip = false; |
|
2783 if (aLayerState != LAYER_NONE) { |
|
2784 DisplayItemData *data = GetDisplayItemDataForManager(aItem, layer->Manager()); |
|
2785 if (data) { |
|
2786 tempManager = data->mInactiveManager; |
|
2787 } |
|
2788 if (!tempManager) { |
|
2789 tempManager = new BasicLayerManager(); |
|
2790 } |
|
2791 |
|
2792 // We need to grab these before calling AddLayerDisplayItem because it will overwrite them. |
|
2793 nsRegion clip; |
|
2794 DisplayItemClip* oldClip = nullptr; |
|
2795 GetOldLayerFor(aItem, nullptr, &oldClip); |
|
2796 hasClip = aClip.ComputeRegionInClips(oldClip, |
|
2797 aTopLeft - thebesData->mLastAnimatedGeometryRootOrigin, |
|
2798 &clip); |
|
2799 |
|
2800 if (hasClip) { |
|
2801 intClip = clip.GetBounds().ScaleToOutsidePixels(thebesData->mXScale, |
|
2802 thebesData->mYScale, |
|
2803 thebesData->mAppUnitsPerDevPixel); |
|
2804 } |
|
2805 } |
|
2806 |
|
2807 AddLayerDisplayItem(layer, aItem, aClip, aLayerState, aTopLeft, tempManager, aGeometry); |
|
2808 |
|
2809 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(layer); |
|
2810 if (entry) { |
|
2811 entry->mContainerLayerFrame = aContainerLayerFrame; |
|
2812 if (entry->mContainerLayerGeneration == 0) { |
|
2813 entry->mContainerLayerGeneration = mContainerLayerGeneration; |
|
2814 } |
|
2815 if (tempManager) { |
|
2816 FrameLayerBuilder* layerBuilder = new FrameLayerBuilder(); |
|
2817 layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData); |
|
2818 |
|
2819 tempManager->BeginTransaction(); |
|
2820 if (mRetainingManager) { |
|
2821 layerBuilder->DidBeginRetainedLayerTransaction(tempManager); |
|
2822 } |
|
2823 |
|
2824 nsAutoPtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot())); |
|
2825 nsRefPtr<Layer> tmpLayer = |
|
2826 aItem->BuildLayer(mDisplayListBuilder, tempManager, ContainerLayerParameters()); |
|
2827 // We have no easy way of detecting if this transaction will ever actually get finished. |
|
2828 // For now, I've just silenced the warning with nested transactions in BasicLayers.cpp |
|
2829 if (!tmpLayer) { |
|
2830 tempManager->EndTransaction(nullptr, nullptr); |
|
2831 tempManager->SetUserData(&gLayerManagerLayerBuilder, nullptr); |
|
2832 return; |
|
2833 } |
|
2834 |
|
2835 // If BuildLayer didn't call BuildContainerLayerFor, then our new layer won't have been |
|
2836 // stored in layerBuilder. Manually add it now. |
|
2837 if (mRetainingManager) { |
|
2838 #ifdef DEBUG_DISPLAY_ITEM_DATA |
|
2839 LayerManagerData* parentLmd = static_cast<LayerManagerData*> |
|
2840 (layer->Manager()->GetUserData(&gLayerManagerUserData)); |
|
2841 LayerManagerData* lmd = static_cast<LayerManagerData*> |
|
2842 (tempManager->GetUserData(&gLayerManagerUserData)); |
|
2843 lmd->mParent = parentLmd; |
|
2844 #endif |
|
2845 layerBuilder->StoreDataForFrame(aItem, tmpLayer, LAYER_ACTIVE); |
|
2846 } |
|
2847 |
|
2848 tempManager->SetRoot(tmpLayer); |
|
2849 layerBuilder->WillEndTransaction(); |
|
2850 tempManager->AbortTransaction(); |
|
2851 |
|
2852 nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForThebesLayer(layer); |
|
2853 props->MoveBy(-offset); |
|
2854 nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr); |
|
2855 if (aLayerState == LAYER_SVG_EFFECTS) { |
|
2856 invalid = nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(aItem->Frame(), |
|
2857 aItem->ToReferenceFrame(), |
|
2858 invalid); |
|
2859 } |
|
2860 if (!invalid.IsEmpty()) { |
|
2861 #ifdef MOZ_DUMP_PAINTING |
|
2862 if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) { |
|
2863 printf_stderr("Inactive LayerManager(%p) for display item %s(%p) has an invalid region - invalidating layer %p\n", tempManager.get(), aItem->Name(), aItem->Frame(), layer); |
|
2864 } |
|
2865 #endif |
|
2866 if (hasClip) { |
|
2867 invalid.And(invalid, intClip); |
|
2868 } |
|
2869 |
|
2870 invalid.ScaleRoundOut(thebesData->mXScale, thebesData->mYScale); |
|
2871 InvalidatePostTransformRegion(layer, invalid, |
|
2872 GetTranslationForThebesLayer(layer)); |
|
2873 } |
|
2874 } |
|
2875 ClippedDisplayItem* cdi = |
|
2876 entry->mItems.AppendElement(ClippedDisplayItem(aItem, |
|
2877 mContainerLayerGeneration)); |
|
2878 cdi->mInactiveLayerManager = tempManager; |
|
2879 } |
|
2880 } |
|
2881 |
|
2882 FrameLayerBuilder::DisplayItemData* |
|
2883 FrameLayerBuilder::StoreDataForFrame(nsDisplayItem* aItem, Layer* aLayer, LayerState aState) |
|
2884 { |
|
2885 DisplayItemData* oldData = GetDisplayItemDataForManager(aItem, mRetainingManager); |
|
2886 if (oldData) { |
|
2887 if (!oldData->mUsed) { |
|
2888 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration, aItem); |
|
2889 } |
|
2890 return oldData; |
|
2891 } |
|
2892 |
|
2893 LayerManagerData* lmd = static_cast<LayerManagerData*> |
|
2894 (mRetainingManager->GetUserData(&gLayerManagerUserData)); |
|
2895 |
|
2896 nsRefPtr<DisplayItemData> data = |
|
2897 new DisplayItemData(lmd, aItem->GetPerFrameKey(), |
|
2898 aLayer, aState, mContainerLayerGeneration); |
|
2899 |
|
2900 data->AddFrame(aItem->Frame()); |
|
2901 |
|
2902 nsAutoTArray<nsIFrame*,4> mergedFrames; |
|
2903 aItem->GetMergedFrames(&mergedFrames); |
|
2904 |
|
2905 for (uint32_t i = 0; i < mergedFrames.Length(); ++i) { |
|
2906 data->AddFrame(mergedFrames[i]); |
|
2907 } |
|
2908 |
|
2909 lmd->mDisplayItems.PutEntry(data); |
|
2910 return data; |
|
2911 } |
|
2912 |
|
2913 void |
|
2914 FrameLayerBuilder::StoreDataForFrame(nsIFrame* aFrame, |
|
2915 uint32_t aDisplayItemKey, |
|
2916 Layer* aLayer, |
|
2917 LayerState aState) |
|
2918 { |
|
2919 DisplayItemData* oldData = GetDisplayItemData(aFrame, aDisplayItemKey); |
|
2920 if (oldData && oldData->mFrameList.Length() == 1) { |
|
2921 oldData->UpdateContents(aLayer, aState, mContainerLayerGeneration); |
|
2922 return; |
|
2923 } |
|
2924 |
|
2925 LayerManagerData* lmd = static_cast<LayerManagerData*> |
|
2926 (mRetainingManager->GetUserData(&gLayerManagerUserData)); |
|
2927 |
|
2928 nsRefPtr<DisplayItemData> data = |
|
2929 new DisplayItemData(lmd, aDisplayItemKey, aLayer, |
|
2930 aState, mContainerLayerGeneration); |
|
2931 |
|
2932 data->AddFrame(aFrame); |
|
2933 |
|
2934 lmd->mDisplayItems.PutEntry(data); |
|
2935 } |
|
2936 |
|
2937 FrameLayerBuilder::ClippedDisplayItem::~ClippedDisplayItem() |
|
2938 { |
|
2939 if (mInactiveLayerManager) { |
|
2940 BasicLayerManager* basic = static_cast<BasicLayerManager*>(mInactiveLayerManager.get()); |
|
2941 basic->SetUserData(&gLayerManagerLayerBuilder, nullptr); |
|
2942 } |
|
2943 } |
|
2944 |
|
2945 void |
|
2946 FrameLayerBuilder::AddLayerDisplayItem(Layer* aLayer, |
|
2947 nsDisplayItem* aItem, |
|
2948 const DisplayItemClip& aClip, |
|
2949 LayerState aLayerState, |
|
2950 const nsPoint& aTopLeft, |
|
2951 BasicLayerManager* aManager, |
|
2952 nsAutoPtr<nsDisplayItemGeometry> aGeometry) |
|
2953 { |
|
2954 if (aLayer->Manager() != mRetainingManager) |
|
2955 return; |
|
2956 |
|
2957 DisplayItemData *data = StoreDataForFrame(aItem, aLayer, aLayerState); |
|
2958 ThebesLayer *t = aLayer->AsThebesLayer(); |
|
2959 if (t) { |
|
2960 data->mGeometry = aGeometry; |
|
2961 data->mClip = aClip; |
|
2962 } |
|
2963 data->mInactiveManager = aManager; |
|
2964 } |
|
2965 |
|
2966 nsIntPoint |
|
2967 FrameLayerBuilder::GetLastPaintOffset(ThebesLayer* aLayer) |
|
2968 { |
|
2969 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); |
|
2970 if (entry) { |
|
2971 if (entry->mContainerLayerGeneration == 0) { |
|
2972 entry->mContainerLayerGeneration = mContainerLayerGeneration; |
|
2973 } |
|
2974 if (entry->mHasExplicitLastPaintOffset) |
|
2975 return entry->mLastPaintOffset; |
|
2976 } |
|
2977 return GetTranslationForThebesLayer(aLayer); |
|
2978 } |
|
2979 |
|
2980 void |
|
2981 FrameLayerBuilder::SaveLastPaintOffset(ThebesLayer* aLayer) |
|
2982 { |
|
2983 ThebesLayerItemsEntry* entry = mThebesLayerItems.PutEntry(aLayer); |
|
2984 if (entry) { |
|
2985 if (entry->mContainerLayerGeneration == 0) { |
|
2986 entry->mContainerLayerGeneration = mContainerLayerGeneration; |
|
2987 } |
|
2988 entry->mLastPaintOffset = GetTranslationForThebesLayer(aLayer); |
|
2989 entry->mHasExplicitLastPaintOffset = true; |
|
2990 } |
|
2991 } |
|
2992 |
|
2993 bool |
|
2994 FrameLayerBuilder::CheckInLayerTreeCompressionMode() |
|
2995 { |
|
2996 if (mInLayerTreeCompressionMode) { |
|
2997 return true; |
|
2998 } |
|
2999 |
|
3000 // If we wanted to be in layer tree compression mode, but weren't, then scheduled |
|
3001 // a delayed repaint where we will be. |
|
3002 mRootPresContext->PresShell()->GetRootFrame()->SchedulePaint(nsIFrame::PAINT_DELAYED_COMPRESS); |
|
3003 |
|
3004 return false; |
|
3005 } |
|
3006 |
|
3007 void |
|
3008 ContainerState::CollectOldLayers() |
|
3009 { |
|
3010 for (Layer* layer = mContainerLayer->GetFirstChild(); layer; |
|
3011 layer = layer->GetNextSibling()) { |
|
3012 NS_ASSERTION(!layer->HasUserData(&gMaskLayerUserData), |
|
3013 "Mask layer in layer tree; could not be recycled."); |
|
3014 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) { |
|
3015 NS_ASSERTION(layer->AsThebesLayer(), "Wrong layer type"); |
|
3016 mRecycledThebesLayers.AppendElement(static_cast<ThebesLayer*>(layer)); |
|
3017 } |
|
3018 |
|
3019 if (Layer* maskLayer = layer->GetMaskLayer()) { |
|
3020 NS_ASSERTION(maskLayer->GetType() == Layer::TYPE_IMAGE, |
|
3021 "Could not recycle mask layer, unsupported layer type."); |
|
3022 mRecycledMaskImageLayers.Put(layer, static_cast<ImageLayer*>(maskLayer)); |
|
3023 } |
|
3024 } |
|
3025 } |
|
3026 |
|
3027 void |
|
3028 ContainerState::Finish(uint32_t* aTextContentFlags, LayerManagerData* aData) |
|
3029 { |
|
3030 while (!mThebesLayerDataStack.IsEmpty()) { |
|
3031 PopThebesLayerData(); |
|
3032 } |
|
3033 |
|
3034 uint32_t textContentFlags = 0; |
|
3035 |
|
3036 // Make sure that current/existing layers are added to the parent and are |
|
3037 // in the correct order. |
|
3038 Layer* layer = nullptr; |
|
3039 for (uint32_t i = 0; i < mNewChildLayers.Length(); ++i) { |
|
3040 Layer* prevChild = i == 0 ? nullptr : mNewChildLayers[i - 1].get(); |
|
3041 layer = mNewChildLayers[i]; |
|
3042 |
|
3043 if (!layer->GetVisibleRegion().IsEmpty()) { |
|
3044 textContentFlags |= layer->GetContentFlags() & Layer::CONTENT_COMPONENT_ALPHA; |
|
3045 } |
|
3046 |
|
3047 if (!layer->GetParent()) { |
|
3048 // This is not currently a child of the container, so just add it |
|
3049 // now. |
|
3050 mContainerLayer->InsertAfter(layer, prevChild); |
|
3051 continue; |
|
3052 } |
|
3053 |
|
3054 NS_ASSERTION(layer->GetParent() == mContainerLayer, |
|
3055 "Layer shouldn't be the child of some other container"); |
|
3056 if (layer->GetPrevSibling() != prevChild) { |
|
3057 mContainerLayer->RepositionChild(layer, prevChild); |
|
3058 } |
|
3059 } |
|
3060 |
|
3061 // Remove old layers that have become unused. |
|
3062 if (!layer) { |
|
3063 layer = mContainerLayer->GetFirstChild(); |
|
3064 } else { |
|
3065 layer = layer->GetNextSibling(); |
|
3066 } |
|
3067 while (layer) { |
|
3068 Layer *layerToRemove = layer; |
|
3069 layer = layer->GetNextSibling(); |
|
3070 mContainerLayer->RemoveChild(layerToRemove); |
|
3071 } |
|
3072 |
|
3073 *aTextContentFlags = textContentFlags; |
|
3074 } |
|
3075 |
|
3076 static inline gfxSize RoundToFloatPrecision(const gfxSize& aSize) |
|
3077 { |
|
3078 return gfxSize(float(aSize.width), float(aSize.height)); |
|
3079 } |
|
3080 |
|
3081 static bool |
|
3082 ChooseScaleAndSetTransform(FrameLayerBuilder* aLayerBuilder, |
|
3083 nsDisplayListBuilder* aDisplayListBuilder, |
|
3084 nsIFrame* aContainerFrame, |
|
3085 const gfx3DMatrix* aTransform, |
|
3086 const ContainerLayerParameters& aIncomingScale, |
|
3087 ContainerLayer* aLayer, |
|
3088 LayerState aState, |
|
3089 ContainerLayerParameters& aOutgoingScale) |
|
3090 { |
|
3091 nsIntPoint offset; |
|
3092 |
|
3093 gfx3DMatrix transform = |
|
3094 gfx3DMatrix::ScalingMatrix(aIncomingScale.mXScale, aIncomingScale.mYScale, 1.0); |
|
3095 if (aTransform) { |
|
3096 // aTransform is applied first, then the scale is applied to the result |
|
3097 transform = (*aTransform)*transform; |
|
3098 // Set any matrix entries close to integers to be those exact integers. |
|
3099 // This protects against floating-point inaccuracies causing problems |
|
3100 // in the checks below. |
|
3101 transform.NudgeToIntegers(); |
|
3102 } |
|
3103 gfxMatrix transform2d; |
|
3104 if (aContainerFrame && |
|
3105 (aState == LAYER_INACTIVE || aState == LAYER_SVG_EFFECTS) && |
|
3106 (!aTransform || (aTransform->Is2D(&transform2d) && |
|
3107 !transform2d.HasNonTranslation()))) { |
|
3108 // When we have an inactive ContainerLayer, translate the container by the offset to the |
|
3109 // reference frame (and offset all child layers by the reverse) so that the coordinate |
|
3110 // space of the child layers isn't affected by scrolling. |
|
3111 // This gets confusing for complicated transform (since we'd have to compute the scale |
|
3112 // factors for the matrix), so we don't bother. Any frames that are building an nsDisplayTransform |
|
3113 // for a css transform would have 0,0 as their offset to the reference frame, so this doesn't |
|
3114 // matter. |
|
3115 nsPoint appUnitOffset = aDisplayListBuilder->ToReferenceFrame(aContainerFrame); |
|
3116 nscoord appUnitsPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); |
|
3117 offset = nsIntPoint( |
|
3118 NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.x, appUnitsPerDevPixel)*aIncomingScale.mXScale), |
|
3119 NS_lround(NSAppUnitsToDoublePixels(appUnitOffset.y, appUnitsPerDevPixel)*aIncomingScale.mYScale)); |
|
3120 } |
|
3121 transform = transform * gfx3DMatrix::Translation(offset.x + aIncomingScale.mOffset.x, offset.y + aIncomingScale.mOffset.y, 0); |
|
3122 |
|
3123 if (transform.IsSingular()) { |
|
3124 return false; |
|
3125 } |
|
3126 |
|
3127 bool canDraw2D = transform.CanDraw2D(&transform2d); |
|
3128 gfxSize scale; |
|
3129 // XXX Should we do something for 3D transforms? |
|
3130 if (canDraw2D) { |
|
3131 // If the container's transform is animated off main thread, fix a suitable scale size |
|
3132 // for animation |
|
3133 if (aContainerFrame->GetContent() && |
|
3134 nsLayoutUtils::HasAnimationsForCompositor( |
|
3135 aContainerFrame->GetContent(), eCSSProperty_transform)) { |
|
3136 scale = nsLayoutUtils::ComputeSuitableScaleForAnimation(aContainerFrame->GetContent()); |
|
3137 } else { |
|
3138 // Scale factors are normalized to a power of 2 to reduce the number of resolution changes |
|
3139 scale = RoundToFloatPrecision(transform2d.ScaleFactors(true)); |
|
3140 // For frames with a changing transform that's not just a translation, |
|
3141 // round scale factors up to nearest power-of-2 boundary so that we don't |
|
3142 // keep having to redraw the content as it scales up and down. Rounding up to nearest |
|
3143 // power-of-2 boundary ensures we never scale up, only down --- avoiding |
|
3144 // jaggies. It also ensures we never scale down by more than a factor of 2, |
|
3145 // avoiding bad downscaling quality. |
|
3146 gfxMatrix frameTransform; |
|
3147 if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform) && |
|
3148 aTransform && |
|
3149 (!aTransform->Is2D(&frameTransform) || frameTransform.HasNonTranslationOrFlip())) { |
|
3150 // Don't clamp the scale factor when the new desired scale factor matches the old one |
|
3151 // or it was previously unscaled. |
|
3152 bool clamp = true; |
|
3153 Matrix oldFrameTransform2d; |
|
3154 if (aLayer->GetBaseTransform().Is2D(&oldFrameTransform2d)) { |
|
3155 gfxSize oldScale = RoundToFloatPrecision(ThebesMatrix(oldFrameTransform2d).ScaleFactors(true)); |
|
3156 if (oldScale == scale || oldScale == gfxSize(1.0, 1.0)) { |
|
3157 clamp = false; |
|
3158 } |
|
3159 } |
|
3160 if (clamp) { |
|
3161 scale.width = gfxUtils::ClampToScaleFactor(scale.width); |
|
3162 scale.height = gfxUtils::ClampToScaleFactor(scale.height); |
|
3163 } |
|
3164 } else { |
|
3165 // XXX Do we need to move nearly-integer values to integers here? |
|
3166 } |
|
3167 } |
|
3168 // If the scale factors are too small, just use 1.0. The content is being |
|
3169 // scaled out of sight anyway. |
|
3170 if (fabs(scale.width) < 1e-8 || fabs(scale.height) < 1e-8) { |
|
3171 scale = gfxSize(1.0, 1.0); |
|
3172 } |
|
3173 } else { |
|
3174 scale = gfxSize(1.0, 1.0); |
|
3175 } |
|
3176 |
|
3177 // Store the inverse of our resolution-scale on the layer |
|
3178 Matrix4x4 baseTransform; |
|
3179 ToMatrix4x4(transform, baseTransform); |
|
3180 aLayer->SetBaseTransform(baseTransform); |
|
3181 aLayer->SetPreScale(1.0f/float(scale.width), |
|
3182 1.0f/float(scale.height)); |
|
3183 aLayer->SetInheritedScale(aIncomingScale.mXScale, |
|
3184 aIncomingScale.mYScale); |
|
3185 |
|
3186 aOutgoingScale = |
|
3187 ContainerLayerParameters(scale.width, scale.height, -offset, aIncomingScale); |
|
3188 if (aTransform) { |
|
3189 aOutgoingScale.mInTransformedSubtree = true; |
|
3190 if (ActiveLayerTracker::IsStyleAnimated(aContainerFrame, eCSSProperty_transform)) { |
|
3191 aOutgoingScale.mInActiveTransformedSubtree = true; |
|
3192 } |
|
3193 } |
|
3194 bool isRetained = aLayer->Manager()->IsWidgetLayerManager(); |
|
3195 if (isRetained && (!canDraw2D || transform2d.HasNonIntegerTranslation())) { |
|
3196 aOutgoingScale.mDisableSubpixelAntialiasingInDescendants = true; |
|
3197 } |
|
3198 return true; |
|
3199 } |
|
3200 |
|
3201 /* static */ PLDHashOperator |
|
3202 FrameLayerBuilder::RestoreDisplayItemData(nsRefPtrHashKey<DisplayItemData>* aEntry, void* aUserArg) |
|
3203 { |
|
3204 DisplayItemData* data = aEntry->GetKey(); |
|
3205 uint32_t *generation = static_cast<uint32_t*>(aUserArg); |
|
3206 |
|
3207 if (data->mUsed && data->mContainerLayerGeneration >= *generation) { |
|
3208 return PL_DHASH_REMOVE; |
|
3209 } |
|
3210 |
|
3211 return PL_DHASH_NEXT; |
|
3212 } |
|
3213 |
|
3214 /* static */ PLDHashOperator |
|
3215 FrameLayerBuilder::RestoreThebesLayerItemEntries(ThebesLayerItemsEntry* aEntry, void* aUserArg) |
|
3216 { |
|
3217 uint32_t *generation = static_cast<uint32_t*>(aUserArg); |
|
3218 |
|
3219 if (aEntry->mContainerLayerGeneration >= *generation) { |
|
3220 // We can just remove these items rather than attempting to revert them |
|
3221 // because we're going to want to invalidate everything when transitioning |
|
3222 // to component alpha flattening. |
|
3223 return PL_DHASH_REMOVE; |
|
3224 } |
|
3225 |
|
3226 for (uint32_t i = 0; i < aEntry->mItems.Length(); i++) { |
|
3227 if (aEntry->mItems[i].mContainerLayerGeneration >= *generation) { |
|
3228 aEntry->mItems.TruncateLength(i); |
|
3229 return PL_DHASH_NEXT; |
|
3230 } |
|
3231 } |
|
3232 |
|
3233 return PL_DHASH_NEXT; |
|
3234 } |
|
3235 |
|
3236 already_AddRefed<ContainerLayer> |
|
3237 FrameLayerBuilder::BuildContainerLayerFor(nsDisplayListBuilder* aBuilder, |
|
3238 LayerManager* aManager, |
|
3239 nsIFrame* aContainerFrame, |
|
3240 nsDisplayItem* aContainerItem, |
|
3241 const nsDisplayList& aChildren, |
|
3242 const ContainerLayerParameters& aParameters, |
|
3243 const gfx3DMatrix* aTransform, |
|
3244 uint32_t aFlags) |
|
3245 { |
|
3246 uint32_t containerDisplayItemKey = |
|
3247 aContainerItem ? aContainerItem->GetPerFrameKey() : nsDisplayItem::TYPE_ZERO; |
|
3248 NS_ASSERTION(aContainerFrame, "Container display items here should have a frame"); |
|
3249 NS_ASSERTION(!aContainerItem || |
|
3250 aContainerItem->Frame() == aContainerFrame, |
|
3251 "Container display item must match given frame"); |
|
3252 |
|
3253 if (!aParameters.mXScale || !aParameters.mYScale) { |
|
3254 return nullptr; |
|
3255 } |
|
3256 |
|
3257 nsRefPtr<ContainerLayer> containerLayer; |
|
3258 if (aManager == mRetainingManager) { |
|
3259 // Using GetOldLayerFor will search merged frames, as well as the underlying |
|
3260 // frame. The underlying frame can change when a page scrolls, so this |
|
3261 // avoids layer recreation in the situation that a new underlying frame is |
|
3262 // picked for a layer. |
|
3263 Layer* oldLayer = nullptr; |
|
3264 if (aContainerItem) { |
|
3265 oldLayer = GetOldLayerFor(aContainerItem); |
|
3266 } else { |
|
3267 DisplayItemData *data = GetOldLayerForFrame(aContainerFrame, containerDisplayItemKey); |
|
3268 if (data) { |
|
3269 oldLayer = data->mLayer; |
|
3270 } |
|
3271 } |
|
3272 |
|
3273 if (oldLayer) { |
|
3274 NS_ASSERTION(oldLayer->Manager() == aManager, "Wrong manager"); |
|
3275 if (oldLayer->HasUserData(&gThebesDisplayItemLayerUserData)) { |
|
3276 // The old layer for this item is actually our ThebesLayer |
|
3277 // because we rendered its layer into that ThebesLayer. So we |
|
3278 // don't actually have a retained container layer. |
|
3279 } else { |
|
3280 NS_ASSERTION(oldLayer->GetType() == Layer::TYPE_CONTAINER, |
|
3281 "Wrong layer type"); |
|
3282 containerLayer = static_cast<ContainerLayer*>(oldLayer); |
|
3283 containerLayer->SetMaskLayer(nullptr); |
|
3284 } |
|
3285 } |
|
3286 } |
|
3287 if (!containerLayer) { |
|
3288 // No suitable existing layer was found. |
|
3289 containerLayer = aManager->CreateContainerLayer(); |
|
3290 if (!containerLayer) |
|
3291 return nullptr; |
|
3292 } |
|
3293 |
|
3294 LayerState state = aContainerItem ? aContainerItem->GetLayerState(aBuilder, aManager, aParameters) : LAYER_ACTIVE; |
|
3295 if (state == LAYER_INACTIVE && |
|
3296 nsDisplayItem::ForceActiveLayers()) { |
|
3297 state = LAYER_ACTIVE; |
|
3298 } |
|
3299 |
|
3300 if (aContainerItem && state == LAYER_ACTIVE_EMPTY) { |
|
3301 // Empty layers only have metadata and should never have display items. We |
|
3302 // early exit because later, invalidation will walk up the frame tree to |
|
3303 // determine which thebes layer gets invalidated. Since an empty layer |
|
3304 // should never have anything to paint, it should never be invalidated. |
|
3305 NS_ASSERTION(aChildren.IsEmpty(), "Should have no children"); |
|
3306 return containerLayer.forget(); |
|
3307 } |
|
3308 |
|
3309 ContainerLayerParameters scaleParameters; |
|
3310 if (!ChooseScaleAndSetTransform(this, aBuilder, aContainerFrame, aTransform, aParameters, |
|
3311 containerLayer, state, scaleParameters)) { |
|
3312 return nullptr; |
|
3313 } |
|
3314 |
|
3315 uint32_t oldGeneration = mContainerLayerGeneration; |
|
3316 mContainerLayerGeneration = ++mMaxContainerLayerGeneration; |
|
3317 |
|
3318 nsRefPtr<RefCountedRegion> thebesLayerInvalidRegion = nullptr; |
|
3319 if (mRetainingManager) { |
|
3320 if (aContainerItem) { |
|
3321 StoreDataForFrame(aContainerItem, containerLayer, LAYER_ACTIVE); |
|
3322 } else { |
|
3323 StoreDataForFrame(aContainerFrame, containerDisplayItemKey, containerLayer, LAYER_ACTIVE); |
|
3324 } |
|
3325 } |
|
3326 |
|
3327 LayerManagerData* data = static_cast<LayerManagerData*> |
|
3328 (aManager->GetUserData(&gLayerManagerUserData)); |
|
3329 |
|
3330 nsRect bounds; |
|
3331 nsIntRect pixBounds; |
|
3332 int32_t appUnitsPerDevPixel; |
|
3333 uint32_t stateFlags = 0; |
|
3334 if ((aContainerFrame->GetStateBits() & NS_FRAME_NO_COMPONENT_ALPHA) && |
|
3335 mRetainingManager && !mRetainingManager->AreComponentAlphaLayersEnabled()) { |
|
3336 stateFlags = ContainerState::NO_COMPONENT_ALPHA; |
|
3337 } |
|
3338 uint32_t flags; |
|
3339 while (true) { |
|
3340 ContainerState state(aBuilder, aManager, aManager->GetLayerBuilder(), |
|
3341 aContainerFrame, aContainerItem, |
|
3342 containerLayer, scaleParameters); |
|
3343 |
|
3344 state.ProcessDisplayItems(aChildren, stateFlags); |
|
3345 |
|
3346 // Set CONTENT_COMPONENT_ALPHA if any of our children have it. |
|
3347 // This is suboptimal ... a child could have text that's over transparent |
|
3348 // pixels in its own layer, but over opaque parts of previous siblings. |
|
3349 state.Finish(&flags, data); |
|
3350 bounds = state.GetChildrenBounds(); |
|
3351 pixBounds = state.ScaleToOutsidePixels(bounds, false); |
|
3352 appUnitsPerDevPixel = state.GetAppUnitsPerDevPixel(); |
|
3353 |
|
3354 if ((flags & Layer::CONTENT_COMPONENT_ALPHA) && |
|
3355 mRetainingManager && |
|
3356 !mRetainingManager->AreComponentAlphaLayersEnabled() && |
|
3357 !stateFlags) { |
|
3358 // Since we don't want any component alpha layers on BasicLayers, we repeat |
|
3359 // the layer building process with this explicitely forced off. |
|
3360 // We restore the previous FrameLayerBuilder state since the first set |
|
3361 // of layer building will have changed it. |
|
3362 stateFlags = ContainerState::NO_COMPONENT_ALPHA; |
|
3363 data->mDisplayItems.EnumerateEntries(RestoreDisplayItemData, |
|
3364 &mContainerLayerGeneration); |
|
3365 mThebesLayerItems.EnumerateEntries(RestoreThebesLayerItemEntries, |
|
3366 &mContainerLayerGeneration); |
|
3367 aContainerFrame->AddStateBits(NS_FRAME_NO_COMPONENT_ALPHA); |
|
3368 continue; |
|
3369 } |
|
3370 break; |
|
3371 } |
|
3372 |
|
3373 NS_ASSERTION(bounds.IsEqualInterior(aChildren.GetBounds(aBuilder)), "Wrong bounds"); |
|
3374 pixBounds.MoveBy(nsIntPoint(scaleParameters.mOffset.x, scaleParameters.mOffset.y)); |
|
3375 if (aParameters.mAncestorClipRect && !(aFlags & CONTAINER_NOT_CLIPPED_BY_ANCESTORS)) { |
|
3376 SetVisibleRegionForLayer(containerLayer, nsIntRegion(pixBounds), |
|
3377 *aParameters.mAncestorClipRect); |
|
3378 } else { |
|
3379 containerLayer->SetVisibleRegion(pixBounds); |
|
3380 } |
|
3381 // Make sure that rounding the visible region out didn't add any area |
|
3382 // we won't paint |
|
3383 if (aChildren.IsOpaque() && !aChildren.NeedsTransparentSurface()) { |
|
3384 bounds.ScaleRoundIn(scaleParameters.mXScale, scaleParameters.mYScale); |
|
3385 if (bounds.Contains(pixBounds.ToAppUnits(appUnitsPerDevPixel))) { |
|
3386 // Clear CONTENT_COMPONENT_ALPHA |
|
3387 flags = Layer::CONTENT_OPAQUE; |
|
3388 } |
|
3389 } |
|
3390 containerLayer->SetContentFlags(flags); |
|
3391 |
|
3392 mContainerLayerGeneration = oldGeneration; |
|
3393 nsPresContext::ClearNotifySubDocInvalidationData(containerLayer); |
|
3394 |
|
3395 return containerLayer.forget(); |
|
3396 } |
|
3397 |
|
3398 Layer* |
|
3399 FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder, |
|
3400 nsDisplayItem* aItem) |
|
3401 { |
|
3402 Layer* layer = GetOldLayerFor(aItem); |
|
3403 if (!layer) |
|
3404 return nullptr; |
|
3405 if (layer->HasUserData(&gThebesDisplayItemLayerUserData)) { |
|
3406 // This layer was created to render Thebes-rendered content for this |
|
3407 // display item. The display item should not use it for its own |
|
3408 // layer rendering. |
|
3409 return nullptr; |
|
3410 } |
|
3411 layer->SetMaskLayer(nullptr); |
|
3412 return layer; |
|
3413 } |
|
3414 |
|
3415 /* static */ void |
|
3416 FrameLayerBuilder::InvalidateAllLayers(LayerManager* aManager) |
|
3417 { |
|
3418 LayerManagerData* data = static_cast<LayerManagerData*> |
|
3419 (aManager->GetUserData(&gLayerManagerUserData)); |
|
3420 if (data) { |
|
3421 data->mInvalidateAllLayers = true; |
|
3422 } |
|
3423 } |
|
3424 |
|
3425 /* static */ void |
|
3426 FrameLayerBuilder::InvalidateAllLayersForFrame(nsIFrame *aFrame) |
|
3427 { |
|
3428 nsTArray<DisplayItemData*> *array = |
|
3429 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
3430 if (array) { |
|
3431 for (uint32_t i = 0; i < array->Length(); i++) { |
|
3432 array->ElementAt(i)->mParent->mInvalidateAllLayers = true; |
|
3433 } |
|
3434 } |
|
3435 } |
|
3436 |
|
3437 /* static */ |
|
3438 Layer* |
|
3439 FrameLayerBuilder::GetDedicatedLayer(nsIFrame* aFrame, uint32_t aDisplayItemKey) |
|
3440 { |
|
3441 //TODO: This isn't completely correct, since a frame could exist as a layer |
|
3442 // in the normal widget manager, and as a different layer (or no layer) |
|
3443 // in the secondary manager |
|
3444 |
|
3445 nsTArray<DisplayItemData*> *array = |
|
3446 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
3447 if (array) { |
|
3448 for (uint32_t i = 0; i < array->Length(); i++) { |
|
3449 DisplayItemData *element = array->ElementAt(i); |
|
3450 if (!element->mParent->mLayerManager->IsWidgetLayerManager()) { |
|
3451 continue; |
|
3452 } |
|
3453 if (element->mDisplayItemKey == aDisplayItemKey) { |
|
3454 if (element->mOptLayer) { |
|
3455 return element->mOptLayer; |
|
3456 } |
|
3457 |
|
3458 Layer* layer = element->mLayer; |
|
3459 if (!layer->HasUserData(&gColorLayerUserData) && |
|
3460 !layer->HasUserData(&gImageLayerUserData) && |
|
3461 !layer->HasUserData(&gThebesDisplayItemLayerUserData)) { |
|
3462 return layer; |
|
3463 } |
|
3464 } |
|
3465 } |
|
3466 } |
|
3467 return nullptr; |
|
3468 } |
|
3469 |
|
3470 static gfxSize |
|
3471 PredictScaleForContent(nsIFrame* aFrame, nsIFrame* aAncestorWithScale, |
|
3472 const gfxSize& aScale) |
|
3473 { |
|
3474 gfx3DMatrix transform = |
|
3475 gfx3DMatrix::ScalingMatrix(aScale.width, aScale.height, 1.0); |
|
3476 if (aFrame != aAncestorWithScale) { |
|
3477 // aTransform is applied first, then the scale is applied to the result |
|
3478 transform = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestorWithScale)*transform; |
|
3479 } |
|
3480 gfxMatrix transform2d; |
|
3481 if (transform.CanDraw2D(&transform2d)) { |
|
3482 return transform2d.ScaleFactors(true); |
|
3483 } |
|
3484 return gfxSize(1.0, 1.0); |
|
3485 } |
|
3486 |
|
3487 gfxSize |
|
3488 FrameLayerBuilder::GetThebesLayerScaleForFrame(nsIFrame* aFrame) |
|
3489 { |
|
3490 nsIFrame* last; |
|
3491 for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { |
|
3492 last = f; |
|
3493 |
|
3494 if (nsLayoutUtils::IsPopup(f)) { |
|
3495 // Don't examine ancestors of a popup. It won't make sense to check |
|
3496 // the transform from some content inside the popup to some content |
|
3497 // which is an ancestor of the popup. |
|
3498 break; |
|
3499 } |
|
3500 |
|
3501 nsTArray<DisplayItemData*> *array = |
|
3502 reinterpret_cast<nsTArray<DisplayItemData*>*>(aFrame->Properties().Get(LayerManagerDataProperty())); |
|
3503 if (!array) { |
|
3504 continue; |
|
3505 } |
|
3506 |
|
3507 for (uint32_t i = 0; i < array->Length(); i++) { |
|
3508 Layer* layer = array->ElementAt(i)->mLayer; |
|
3509 ContainerLayer* container = layer->AsContainerLayer(); |
|
3510 if (!container || |
|
3511 !layer->Manager()->IsWidgetLayerManager()) { |
|
3512 continue; |
|
3513 } |
|
3514 for (Layer* l = container->GetFirstChild(); l; l = l->GetNextSibling()) { |
|
3515 ThebesDisplayItemLayerUserData* data = |
|
3516 static_cast<ThebesDisplayItemLayerUserData*> |
|
3517 (l->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
3518 if (data) { |
|
3519 return PredictScaleForContent(aFrame, f, gfxSize(data->mXScale, data->mYScale)); |
|
3520 } |
|
3521 } |
|
3522 } |
|
3523 } |
|
3524 |
|
3525 return PredictScaleForContent(aFrame, last, |
|
3526 last->PresContext()->PresShell()->GetResolution()); |
|
3527 } |
|
3528 |
|
3529 #ifdef MOZ_DUMP_PAINTING |
|
3530 static void DebugPaintItem(nsRenderingContext* aDest, nsDisplayItem *aItem, nsDisplayListBuilder* aBuilder) |
|
3531 { |
|
3532 bool snap; |
|
3533 nsRect appUnitBounds = aItem->GetBounds(aBuilder, &snap); |
|
3534 gfxRect bounds(appUnitBounds.x, appUnitBounds.y, appUnitBounds.width, appUnitBounds.height); |
|
3535 bounds.ScaleInverse(aDest->AppUnitsPerDevPixel()); |
|
3536 |
|
3537 nsRefPtr<gfxASurface> surf = |
|
3538 gfxPlatform::GetPlatform()->CreateOffscreenSurface(IntSize(bounds.width, bounds.height), |
|
3539 gfxContentType::COLOR_ALPHA); |
|
3540 surf->SetDeviceOffset(-bounds.TopLeft()); |
|
3541 nsRefPtr<gfxContext> context = new gfxContext(surf); |
|
3542 nsRefPtr<nsRenderingContext> ctx = new nsRenderingContext(); |
|
3543 ctx->Init(aDest->DeviceContext(), context); |
|
3544 |
|
3545 aItem->Paint(aBuilder, ctx); |
|
3546 DumpPaintedImage(aItem, surf); |
|
3547 aItem->SetPainted(); |
|
3548 |
|
3549 surf->SetDeviceOffset(gfxPoint(0, 0)); |
|
3550 aDest->ThebesContext()->SetSource(surf, bounds.TopLeft()); |
|
3551 aDest->ThebesContext()->Rectangle(bounds); |
|
3552 aDest->ThebesContext()->Fill(); |
|
3553 } |
|
3554 #endif |
|
3555 |
|
3556 /* static */ void |
|
3557 FrameLayerBuilder::RecomputeVisibilityForItems(nsTArray<ClippedDisplayItem>& aItems, |
|
3558 nsDisplayListBuilder *aBuilder, |
|
3559 const nsIntRegion& aRegionToDraw, |
|
3560 const nsIntPoint& aOffset, |
|
3561 int32_t aAppUnitsPerDevPixel, |
|
3562 float aXScale, |
|
3563 float aYScale) |
|
3564 { |
|
3565 uint32_t i; |
|
3566 // Update visible regions. We need perform visibility analysis again |
|
3567 // because we may be asked to draw into part of a ThebesLayer that |
|
3568 // isn't actually visible in the window (e.g., because a ThebesLayer |
|
3569 // expanded its visible region to a rectangle internally), in which |
|
3570 // case the mVisibleRect stored in the display item may be wrong. |
|
3571 nsRegion visible = aRegionToDraw.ToAppUnits(aAppUnitsPerDevPixel); |
|
3572 visible.MoveBy(NSIntPixelsToAppUnits(aOffset.x, aAppUnitsPerDevPixel), |
|
3573 NSIntPixelsToAppUnits(aOffset.y, aAppUnitsPerDevPixel)); |
|
3574 visible.ScaleInverseRoundOut(aXScale, aYScale); |
|
3575 |
|
3576 for (i = aItems.Length(); i > 0; --i) { |
|
3577 ClippedDisplayItem* cdi = &aItems[i - 1]; |
|
3578 const DisplayItemClip& clip = cdi->mItem->GetClip(); |
|
3579 |
|
3580 NS_ASSERTION(AppUnitsPerDevPixel(cdi->mItem) == aAppUnitsPerDevPixel, |
|
3581 "a thebes layer should contain items only at the same zoom"); |
|
3582 |
|
3583 NS_ABORT_IF_FALSE(clip.HasClip() || |
|
3584 clip.GetRoundedRectCount() == 0, |
|
3585 "If we have rounded rects, we must have a clip rect"); |
|
3586 |
|
3587 if (!clip.IsRectAffectedByClip(visible.GetBounds())) { |
|
3588 cdi->mItem->RecomputeVisibility(aBuilder, &visible); |
|
3589 continue; |
|
3590 } |
|
3591 |
|
3592 // Do a little dance to account for the fact that we're clipping |
|
3593 // to cdi->mClipRect |
|
3594 nsRegion clipped; |
|
3595 clipped.And(visible, clip.NonRoundedIntersection()); |
|
3596 nsRegion finalClipped = clipped; |
|
3597 cdi->mItem->RecomputeVisibility(aBuilder, &finalClipped); |
|
3598 // If we have rounded clip rects, don't subtract from the visible |
|
3599 // region since we aren't displaying everything inside the rect. |
|
3600 if (clip.GetRoundedRectCount() == 0) { |
|
3601 nsRegion removed; |
|
3602 removed.Sub(clipped, finalClipped); |
|
3603 nsRegion newVisible; |
|
3604 newVisible.Sub(visible, removed); |
|
3605 // Don't let the visible region get too complex. |
|
3606 if (newVisible.GetNumRects() <= 15) { |
|
3607 visible = newVisible; |
|
3608 } |
|
3609 } |
|
3610 } |
|
3611 } |
|
3612 |
|
3613 void |
|
3614 FrameLayerBuilder::PaintItems(nsTArray<ClippedDisplayItem>& aItems, |
|
3615 const nsIntRect& aRect, |
|
3616 gfxContext *aContext, |
|
3617 nsRenderingContext *aRC, |
|
3618 nsDisplayListBuilder* aBuilder, |
|
3619 nsPresContext* aPresContext, |
|
3620 const nsIntPoint& aOffset, |
|
3621 float aXScale, float aYScale, |
|
3622 int32_t aCommonClipCount) |
|
3623 { |
|
3624 int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel(); |
|
3625 nsRect boundRect = aRect.ToAppUnits(appUnitsPerDevPixel); |
|
3626 boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel), |
|
3627 NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel)); |
|
3628 boundRect.ScaleInverseRoundOut(aXScale, aYScale); |
|
3629 |
|
3630 DisplayItemClip currentClip; |
|
3631 bool currentClipIsSetInContext = false; |
|
3632 DisplayItemClip tmpClip; |
|
3633 |
|
3634 for (uint32_t i = 0; i < aItems.Length(); ++i) { |
|
3635 ClippedDisplayItem* cdi = &aItems[i]; |
|
3636 |
|
3637 nsRect paintRect = cdi->mItem->GetVisibleRect().Intersect(boundRect); |
|
3638 if (paintRect.IsEmpty()) |
|
3639 continue; |
|
3640 |
|
3641 // If the new desired clip state is different from the current state, |
|
3642 // update the clip. |
|
3643 const DisplayItemClip* clip = &cdi->mItem->GetClip(); |
|
3644 if (clip->GetRoundedRectCount() > 0 && |
|
3645 !clip->IsRectClippedByRoundedCorner(cdi->mItem->GetVisibleRect())) { |
|
3646 tmpClip = *clip; |
|
3647 tmpClip.RemoveRoundedCorners(); |
|
3648 clip = &tmpClip; |
|
3649 } |
|
3650 if (currentClipIsSetInContext != clip->HasClip() || |
|
3651 (clip->HasClip() && *clip != currentClip)) { |
|
3652 if (currentClipIsSetInContext) { |
|
3653 aContext->Restore(); |
|
3654 } |
|
3655 currentClipIsSetInContext = clip->HasClip(); |
|
3656 if (currentClipIsSetInContext) { |
|
3657 currentClip = *clip; |
|
3658 aContext->Save(); |
|
3659 NS_ASSERTION(aCommonClipCount < 100, |
|
3660 "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong."); |
|
3661 currentClip.ApplyTo(aContext, aPresContext, aCommonClipCount); |
|
3662 aContext->NewPath(); |
|
3663 } |
|
3664 } |
|
3665 |
|
3666 if (cdi->mInactiveLayerManager) { |
|
3667 PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aRC); |
|
3668 } else { |
|
3669 nsIFrame* frame = cdi->mItem->Frame(); |
|
3670 frame->AddStateBits(NS_FRAME_PAINTED_THEBES); |
|
3671 #ifdef MOZ_DUMP_PAINTING |
|
3672 |
|
3673 if (gfxUtils::sDumpPainting) { |
|
3674 DebugPaintItem(aRC, cdi->mItem, aBuilder); |
|
3675 } else { |
|
3676 #else |
|
3677 { |
|
3678 #endif |
|
3679 cdi->mItem->Paint(aBuilder, aRC); |
|
3680 } |
|
3681 } |
|
3682 |
|
3683 if (CheckDOMModified()) |
|
3684 break; |
|
3685 } |
|
3686 |
|
3687 if (currentClipIsSetInContext) { |
|
3688 aContext->Restore(); |
|
3689 } |
|
3690 } |
|
3691 |
|
3692 /** |
|
3693 * Returns true if it is preferred to draw the list of display |
|
3694 * items separately for each rect in the visible region rather |
|
3695 * than clipping to a complex region. |
|
3696 */ |
|
3697 static bool ShouldDrawRectsSeparately(gfxContext* aContext, DrawRegionClip aClip) |
|
3698 { |
|
3699 if (!gfxPrefs::LayoutPaintRectsSeparately() || |
|
3700 aContext->IsCairo() || |
|
3701 aClip == DrawRegionClip::CLIP_NONE) { |
|
3702 return false; |
|
3703 } |
|
3704 |
|
3705 DrawTarget *dt = aContext->GetDrawTarget(); |
|
3706 return dt->GetType() == BackendType::DIRECT2D; |
|
3707 } |
|
3708 |
|
3709 static void DrawForcedBackgroundColor(gfxContext* aContext, Layer* aLayer, nscolor aBackgroundColor) |
|
3710 { |
|
3711 if (NS_GET_A(aBackgroundColor) > 0) { |
|
3712 nsIntRect r = aLayer->GetVisibleRegion().GetBounds(); |
|
3713 aContext->NewPath(); |
|
3714 aContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); |
|
3715 aContext->SetColor(gfxRGBA(aBackgroundColor)); |
|
3716 aContext->Fill(); |
|
3717 } |
|
3718 } |
|
3719 |
|
3720 /* |
|
3721 * A note on residual transforms: |
|
3722 * |
|
3723 * In a transformed subtree we sometimes apply the ThebesLayer's |
|
3724 * "residual transform" when drawing content into the ThebesLayer. |
|
3725 * This is a translation by components in the range [-0.5,0.5) provided |
|
3726 * by the layer system; applying the residual transform followed by the |
|
3727 * transforms used by layer compositing ensures that the subpixel alignment |
|
3728 * of the content of the ThebesLayer exactly matches what it would be if |
|
3729 * we used cairo/Thebes to draw directly to the screen without going through |
|
3730 * retained layer buffers. |
|
3731 * |
|
3732 * The visible and valid regions of the ThebesLayer are computed without |
|
3733 * knowing the residual transform (because we don't know what the residual |
|
3734 * transform is going to be until we've built the layer tree!). So we have to |
|
3735 * consider whether content painted in the range [x, xmost) might be painted |
|
3736 * outside the visible region we computed for that content. The visible region |
|
3737 * would be [floor(x), ceil(xmost)). The content would be rendered at |
|
3738 * [x + r, xmost + r), where -0.5 <= r < 0.5. So some half-rendered pixels could |
|
3739 * indeed fall outside the computed visible region, which is not a big deal; |
|
3740 * similar issues already arise when we snap cliprects to nearest pixels. |
|
3741 * Note that if the rendering of the content is snapped to nearest pixels --- |
|
3742 * which it often is --- then the content is actually rendered at |
|
3743 * [snap(x + r), snap(xmost + r)). It turns out that floor(x) <= snap(x + r) |
|
3744 * and ceil(xmost) >= snap(xmost + r), so the rendering of snapped content |
|
3745 * always falls within the visible region we computed. |
|
3746 */ |
|
3747 |
|
3748 /* static */ void |
|
3749 FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer, |
|
3750 gfxContext* aContext, |
|
3751 const nsIntRegion& aRegionToDraw, |
|
3752 DrawRegionClip aClip, |
|
3753 const nsIntRegion& aRegionToInvalidate, |
|
3754 void* aCallbackData) |
|
3755 { |
|
3756 PROFILER_LABEL("gfx", "DrawThebesLayer"); |
|
3757 |
|
3758 nsDisplayListBuilder* builder = static_cast<nsDisplayListBuilder*> |
|
3759 (aCallbackData); |
|
3760 |
|
3761 FrameLayerBuilder *layerBuilder = aLayer->Manager()->GetLayerBuilder(); |
|
3762 NS_ASSERTION(layerBuilder, "Unexpectedly null layer builder!"); |
|
3763 |
|
3764 if (layerBuilder->CheckDOMModified()) |
|
3765 return; |
|
3766 |
|
3767 ThebesLayerItemsEntry* entry = layerBuilder->mThebesLayerItems.GetEntry(aLayer); |
|
3768 NS_ASSERTION(entry, "We shouldn't be drawing into a layer with no items!"); |
|
3769 if (!entry->mContainerLayerFrame) { |
|
3770 return; |
|
3771 } |
|
3772 |
|
3773 |
|
3774 ThebesDisplayItemLayerUserData* userData = |
|
3775 static_cast<ThebesDisplayItemLayerUserData*> |
|
3776 (aLayer->GetUserData(&gThebesDisplayItemLayerUserData)); |
|
3777 NS_ASSERTION(userData, "where did our user data go?"); |
|
3778 |
|
3779 bool shouldDrawRectsSeparately = ShouldDrawRectsSeparately(aContext, aClip); |
|
3780 |
|
3781 if (!shouldDrawRectsSeparately) { |
|
3782 if (aClip == DrawRegionClip::DRAW_SNAPPED) { |
|
3783 gfxUtils::ClipToRegionSnapped(aContext, aRegionToDraw); |
|
3784 } else if (aClip == DrawRegionClip::DRAW) { |
|
3785 gfxUtils::ClipToRegion(aContext, aRegionToDraw); |
|
3786 } |
|
3787 |
|
3788 DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor); |
|
3789 } |
|
3790 |
|
3791 // make the origin of the context coincide with the origin of the |
|
3792 // ThebesLayer |
|
3793 gfxContextMatrixAutoSaveRestore saveMatrix(aContext); |
|
3794 nsIntPoint offset = GetTranslationForThebesLayer(aLayer); |
|
3795 |
|
3796 nsPresContext* presContext = entry->mContainerLayerFrame->PresContext(); |
|
3797 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); |
|
3798 |
|
3799 RecomputeVisibilityForItems(entry->mItems, builder, aRegionToDraw, |
|
3800 offset, appUnitsPerDevPixel, |
|
3801 userData->mXScale, userData->mYScale); |
|
3802 |
|
3803 nsRefPtr<nsRenderingContext> rc = new nsRenderingContext(); |
|
3804 rc->Init(presContext->DeviceContext(), aContext); |
|
3805 |
|
3806 if (shouldDrawRectsSeparately) { |
|
3807 nsIntRegionRectIterator it(aRegionToDraw); |
|
3808 while (const nsIntRect* iterRect = it.Next()) { |
|
3809 gfxContextAutoSaveRestore save(aContext); |
|
3810 aContext->NewPath(); |
|
3811 aContext->Rectangle(*iterRect, aClip == DrawRegionClip::DRAW_SNAPPED); |
|
3812 aContext->Clip(); |
|
3813 |
|
3814 DrawForcedBackgroundColor(aContext, aLayer, userData->mForcedBackgroundColor); |
|
3815 |
|
3816 // Apply the residual transform if it has been enabled, to ensure that |
|
3817 // snapping when we draw into aContext exactly matches the ideal transform. |
|
3818 // See above for why this is OK. |
|
3819 aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)); |
|
3820 aContext->Scale(userData->mXScale, userData->mYScale); |
|
3821 |
|
3822 layerBuilder->PaintItems(entry->mItems, *iterRect, aContext, rc, |
|
3823 builder, presContext, |
|
3824 offset, userData->mXScale, userData->mYScale, |
|
3825 entry->mCommonClipCount); |
|
3826 } |
|
3827 } else { |
|
3828 // Apply the residual transform if it has been enabled, to ensure that |
|
3829 // snapping when we draw into aContext exactly matches the ideal transform. |
|
3830 // See above for why this is OK. |
|
3831 aContext->Translate(aLayer->GetResidualTranslation() - gfxPoint(offset.x, offset.y)); |
|
3832 aContext->Scale(userData->mXScale, userData->mYScale); |
|
3833 |
|
3834 layerBuilder->PaintItems(entry->mItems, aRegionToDraw.GetBounds(), aContext, rc, |
|
3835 builder, presContext, |
|
3836 offset, userData->mXScale, userData->mYScale, |
|
3837 entry->mCommonClipCount); |
|
3838 } |
|
3839 |
|
3840 if (presContext->GetPaintFlashing()) { |
|
3841 FlashPaint(aContext); |
|
3842 } |
|
3843 |
|
3844 if (!aRegionToInvalidate.IsEmpty()) { |
|
3845 aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds()); |
|
3846 } |
|
3847 } |
|
3848 |
|
3849 bool |
|
3850 FrameLayerBuilder::CheckDOMModified() |
|
3851 { |
|
3852 if (!mRootPresContext || |
|
3853 mInitialDOMGeneration == mRootPresContext->GetDOMGeneration()) |
|
3854 return false; |
|
3855 if (mDetectedDOMModification) { |
|
3856 // Don't spam the console with extra warnings |
|
3857 return true; |
|
3858 } |
|
3859 mDetectedDOMModification = true; |
|
3860 // Painting is not going to complete properly. There's not much |
|
3861 // we can do here though. Invalidating the window to get another repaint |
|
3862 // is likely to lead to an infinite repaint loop. |
|
3863 NS_WARNING("Detected DOM modification during paint, bailing out!"); |
|
3864 return true; |
|
3865 } |
|
3866 |
|
3867 #ifdef MOZ_DUMP_PAINTING |
|
3868 /* static */ void |
|
3869 FrameLayerBuilder::DumpRetainedLayerTree(LayerManager* aManager, FILE* aFile, bool aDumpHtml) |
|
3870 { |
|
3871 aManager->Dump(aFile, "", aDumpHtml); |
|
3872 } |
|
3873 #endif |
|
3874 |
|
3875 gfx::Rect |
|
3876 CalculateBounds(const nsTArray<DisplayItemClip::RoundedRect>& aRects, int32_t A2D) |
|
3877 { |
|
3878 nsRect bounds = aRects[0].mRect; |
|
3879 for (uint32_t i = 1; i < aRects.Length(); ++i) { |
|
3880 bounds.UnionRect(bounds, aRects[i].mRect); |
|
3881 } |
|
3882 |
|
3883 return gfx::ToRect(nsLayoutUtils::RectToGfxRect(bounds, A2D)); |
|
3884 } |
|
3885 |
|
3886 static void |
|
3887 SetClipCount(ThebesDisplayItemLayerUserData* aThebesData, |
|
3888 uint32_t aClipCount) |
|
3889 { |
|
3890 if (aThebesData) { |
|
3891 aThebesData->mMaskClipCount = aClipCount; |
|
3892 } |
|
3893 } |
|
3894 |
|
3895 void |
|
3896 ContainerState::SetupMaskLayer(Layer *aLayer, const DisplayItemClip& aClip, |
|
3897 uint32_t aRoundedRectClipCount) |
|
3898 { |
|
3899 // if the number of clips we are going to mask has decreased, then aLayer might have |
|
3900 // cached graphics which assume the existence of a soon-to-be non-existent mask layer |
|
3901 // in that case, invalidate the whole layer. |
|
3902 ThebesDisplayItemLayerUserData* thebesData = GetThebesDisplayItemLayerUserData(aLayer); |
|
3903 if (thebesData && |
|
3904 aRoundedRectClipCount < thebesData->mMaskClipCount) { |
|
3905 ThebesLayer* thebes = aLayer->AsThebesLayer(); |
|
3906 thebes->InvalidateRegion(thebes->GetValidRegion().GetBounds()); |
|
3907 } |
|
3908 |
|
3909 // don't build an unnecessary mask |
|
3910 nsIntRect layerBounds = aLayer->GetVisibleRegion().GetBounds(); |
|
3911 if (aClip.GetRoundedRectCount() == 0 || |
|
3912 aRoundedRectClipCount == 0 || |
|
3913 layerBounds.IsEmpty()) { |
|
3914 SetClipCount(thebesData, 0); |
|
3915 return; |
|
3916 } |
|
3917 |
|
3918 // check if we can re-use the mask layer |
|
3919 nsRefPtr<ImageLayer> maskLayer = CreateOrRecycleMaskImageLayerFor(aLayer); |
|
3920 MaskLayerUserData* userData = GetMaskLayerUserData(maskLayer); |
|
3921 |
|
3922 MaskLayerUserData newData; |
|
3923 aClip.AppendRoundedRects(&newData.mRoundedClipRects, aRoundedRectClipCount); |
|
3924 newData.mScaleX = mParameters.mXScale; |
|
3925 newData.mScaleY = mParameters.mYScale; |
|
3926 newData.mOffset = mParameters.mOffset; |
|
3927 newData.mAppUnitsPerDevPixel = mContainerFrame->PresContext()->AppUnitsPerDevPixel(); |
|
3928 |
|
3929 if (*userData == newData) { |
|
3930 aLayer->SetMaskLayer(maskLayer); |
|
3931 SetClipCount(thebesData, aRoundedRectClipCount); |
|
3932 return; |
|
3933 } |
|
3934 |
|
3935 // calculate a more precise bounding rect |
|
3936 gfx::Rect boundingRect = CalculateBounds(newData.mRoundedClipRects, |
|
3937 newData.mAppUnitsPerDevPixel); |
|
3938 boundingRect.Scale(mParameters.mXScale, mParameters.mYScale); |
|
3939 |
|
3940 uint32_t maxSize = mManager->GetMaxTextureSize(); |
|
3941 NS_ASSERTION(maxSize > 0, "Invalid max texture size"); |
|
3942 gfx::Size surfaceSize(std::min<gfx::Float>(boundingRect.Width(), maxSize), |
|
3943 std::min<gfx::Float>(boundingRect.Height(), maxSize)); |
|
3944 |
|
3945 // maskTransform is applied to the clip when it is painted into the mask (as a |
|
3946 // component of imageTransform), and its inverse used when the mask is used for |
|
3947 // masking. |
|
3948 // It is the transform from the masked layer's space to mask space |
|
3949 gfx::Matrix maskTransform; |
|
3950 maskTransform.Scale(surfaceSize.width/boundingRect.Width(), |
|
3951 surfaceSize.height/boundingRect.Height()); |
|
3952 gfx::Point p = boundingRect.TopLeft(); |
|
3953 maskTransform.Translate(-p.x, -p.y); |
|
3954 // imageTransform is only used when the clip is painted to the mask |
|
3955 gfx::Matrix imageTransform = maskTransform; |
|
3956 imageTransform.Scale(mParameters.mXScale, mParameters.mYScale); |
|
3957 |
|
3958 nsAutoPtr<MaskLayerImageCache::MaskLayerImageKey> newKey( |
|
3959 new MaskLayerImageCache::MaskLayerImageKey()); |
|
3960 |
|
3961 // copy and transform the rounded rects |
|
3962 for (uint32_t i = 0; i < newData.mRoundedClipRects.Length(); ++i) { |
|
3963 newKey->mRoundedClipRects.AppendElement( |
|
3964 MaskLayerImageCache::PixelRoundedRect(newData.mRoundedClipRects[i], |
|
3965 mContainerFrame->PresContext())); |
|
3966 newKey->mRoundedClipRects[i].ScaleAndTranslate(imageTransform); |
|
3967 } |
|
3968 |
|
3969 const MaskLayerImageCache::MaskLayerImageKey* lookupKey = newKey; |
|
3970 |
|
3971 // check to see if we can reuse a mask image |
|
3972 nsRefPtr<ImageContainer> container = |
|
3973 GetMaskLayerImageCache()->FindImageFor(&lookupKey); |
|
3974 |
|
3975 if (!container) { |
|
3976 IntSize surfaceSizeInt(NSToIntCeil(surfaceSize.width), |
|
3977 NSToIntCeil(surfaceSize.height)); |
|
3978 // no existing mask image, so build a new one |
|
3979 RefPtr<DrawTarget> dt = |
|
3980 aLayer->Manager()->CreateOptimalMaskDrawTarget(surfaceSizeInt); |
|
3981 |
|
3982 // fail if we can't get the right surface |
|
3983 if (!dt) { |
|
3984 NS_WARNING("Could not create DrawTarget for mask layer."); |
|
3985 SetClipCount(thebesData, 0); |
|
3986 return; |
|
3987 } |
|
3988 |
|
3989 nsRefPtr<gfxContext> context = new gfxContext(dt); |
|
3990 context->Multiply(ThebesMatrix(imageTransform)); |
|
3991 |
|
3992 // paint the clipping rects with alpha to create the mask |
|
3993 context->SetColor(gfxRGBA(1, 1, 1, 1)); |
|
3994 aClip.DrawRoundedRectsTo(context, |
|
3995 newData.mAppUnitsPerDevPixel, |
|
3996 0, |
|
3997 aRoundedRectClipCount); |
|
3998 |
|
3999 RefPtr<SourceSurface> surface = dt->Snapshot(); |
|
4000 |
|
4001 // build the image and container |
|
4002 container = aLayer->Manager()->CreateImageContainer(); |
|
4003 NS_ASSERTION(container, "Could not create image container for mask layer."); |
|
4004 nsRefPtr<Image> image = container->CreateImage(ImageFormat::CAIRO_SURFACE); |
|
4005 NS_ASSERTION(image, "Could not create image container for mask layer."); |
|
4006 CairoImage::Data data; |
|
4007 data.mSize = surfaceSizeInt; |
|
4008 data.mSourceSurface = surface; |
|
4009 |
|
4010 static_cast<CairoImage*>(image.get())->SetData(data); |
|
4011 container->SetCurrentImageInTransaction(image); |
|
4012 |
|
4013 GetMaskLayerImageCache()->PutImage(newKey.forget(), container); |
|
4014 } |
|
4015 |
|
4016 maskLayer->SetContainer(container); |
|
4017 |
|
4018 maskTransform.Invert(); |
|
4019 Matrix4x4 matrix = Matrix4x4::From2D(maskTransform); |
|
4020 matrix.Translate(mParameters.mOffset.x, mParameters.mOffset.y, 0); |
|
4021 maskLayer->SetBaseTransform(matrix); |
|
4022 |
|
4023 // save the details of the clip in user data |
|
4024 userData->mScaleX = newData.mScaleX; |
|
4025 userData->mScaleY = newData.mScaleY; |
|
4026 userData->mOffset = newData.mOffset; |
|
4027 userData->mAppUnitsPerDevPixel = newData.mAppUnitsPerDevPixel; |
|
4028 userData->mRoundedClipRects.SwapElements(newData.mRoundedClipRects); |
|
4029 userData->mImageKey = lookupKey; |
|
4030 |
|
4031 aLayer->SetMaskLayer(maskLayer); |
|
4032 SetClipCount(thebesData, aRoundedRectClipCount); |
|
4033 return; |
|
4034 } |
|
4035 |
|
4036 } // namespace mozilla |