Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /*-*- Mode: C++; tab-width: 2; 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 file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "LayerTreeInvalidation.h"
7 #include <stdint.h> // for uint32_t
8 #include "ImageContainer.h" // for ImageContainer
9 #include "ImageLayers.h" // for ImageLayer, etc
10 #include "Layers.h" // for Layer, ContainerLayer, etc
11 #include "gfx3DMatrix.h" // for gfx3DMatrix
12 #include "gfxColor.h" // for gfxRGBA
13 #include "GraphicsFilter.h" // for GraphicsFilter
14 #include "gfxPoint3D.h" // for gfxPoint3D
15 #include "gfxRect.h" // for gfxRect
16 #include "gfxUtils.h" // for gfxUtils
17 #include "mozilla/gfx/BaseSize.h" // for BaseSize
18 #include "mozilla/gfx/Point.h" // for IntSize
19 #include "mozilla/mozalloc.h" // for operator new, etc
20 #include "nsAutoPtr.h" // for nsRefPtr, nsAutoPtr, etc
21 #include "nsDataHashtable.h" // for nsDataHashtable
22 #include "nsDebug.h" // for NS_ASSERTION
23 #include "nsHashKeys.h" // for nsPtrHashKey
24 #include "nsISupportsImpl.h" // for Layer::AddRef, etc
25 #include "nsPoint.h" // for nsIntPoint
26 #include "nsRect.h" // for nsIntRect
27 #include "nsTArray.h" // for nsAutoTArray, nsTArray_Impl
29 namespace mozilla {
30 namespace layers {
32 struct LayerPropertiesBase;
33 LayerPropertiesBase* CloneLayerTreePropertiesInternal(Layer* aRoot);
35 static nsIntRect
36 TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform)
37 {
38 if (aRect.IsEmpty()) {
39 return nsIntRect();
40 }
42 gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
43 rect = aTransform.TransformBounds(rect);
44 rect.RoundOut();
46 nsIntRect intRect;
47 if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) {
48 return nsIntRect();
49 }
51 return intRect;
52 }
54 static void
55 AddTransformedRegion(nsIntRegion& aDest, const nsIntRegion& aSource, const gfx3DMatrix& aTransform)
56 {
57 nsIntRegionRectIterator iter(aSource);
58 const nsIntRect *r;
59 while ((r = iter.Next())) {
60 aDest.Or(aDest, TransformRect(*r, aTransform));
61 }
62 aDest.SimplifyOutward(20);
63 }
65 static void
66 AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource)
67 {
68 aDest.Or(aDest, aSource);
69 aDest.SimplifyOutward(20);
70 }
72 static nsIntRegion
73 TransformRegion(const nsIntRegion& aRegion, const gfx3DMatrix& aTransform)
74 {
75 nsIntRegion result;
76 AddTransformedRegion(result, aRegion, aTransform);
77 return result;
78 }
80 /**
81 * Walks over this layer, and all descendant layers.
82 * If any of these are a ContainerLayer that reports invalidations to a PresShell,
83 * then report that the entire bounds have changed.
84 */
85 static void
86 NotifySubdocumentInvalidationRecursive(Layer* aLayer, NotifySubDocInvalidationFunc aCallback)
87 {
88 aLayer->ClearInvalidRect();
89 ContainerLayer* container = aLayer->AsContainerLayer();
91 if (aLayer->GetMaskLayer()) {
92 NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback);
93 }
95 if (!container) {
96 return;
97 }
99 for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
100 NotifySubdocumentInvalidationRecursive(child, aCallback);
101 }
103 aCallback(container, container->GetVisibleRegion());
104 }
106 struct LayerPropertiesBase : public LayerProperties
107 {
108 LayerPropertiesBase(Layer* aLayer)
109 : mLayer(aLayer)
110 , mMaskLayer(nullptr)
111 , mVisibleRegion(aLayer->GetVisibleRegion())
112 , mInvalidRegion(aLayer->GetInvalidRegion())
113 , mOpacity(aLayer->GetLocalOpacity())
114 , mUseClipRect(!!aLayer->GetClipRect())
115 {
116 MOZ_COUNT_CTOR(LayerPropertiesBase);
117 if (aLayer->GetMaskLayer()) {
118 mMaskLayer = CloneLayerTreePropertiesInternal(aLayer->GetMaskLayer());
119 }
120 if (mUseClipRect) {
121 mClipRect = *aLayer->GetClipRect();
122 }
123 gfx::To3DMatrix(aLayer->GetTransform(), mTransform);
124 }
125 LayerPropertiesBase()
126 : mLayer(nullptr)
127 , mMaskLayer(nullptr)
128 {
129 MOZ_COUNT_CTOR(LayerPropertiesBase);
130 }
131 ~LayerPropertiesBase()
132 {
133 MOZ_COUNT_DTOR(LayerPropertiesBase);
134 }
136 virtual nsIntRegion ComputeDifferences(Layer* aRoot,
137 NotifySubDocInvalidationFunc aCallback,
138 bool* aGeometryChanged);
140 virtual void MoveBy(const nsIntPoint& aOffset);
142 nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
143 bool& aGeometryChanged)
144 {
145 gfx3DMatrix transform;
146 gfx::To3DMatrix(mLayer->GetTransform(), transform);
147 bool transformChanged = !mTransform.FuzzyEqual(transform);
148 Layer* otherMask = mLayer->GetMaskLayer();
149 const nsIntRect* otherClip = mLayer->GetClipRect();
150 nsIntRegion result;
151 if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
152 (mUseClipRect != !!otherClip) ||
153 mLayer->GetLocalOpacity() != mOpacity ||
154 transformChanged)
155 {
156 aGeometryChanged = true;
157 result = OldTransformedBounds();
158 if (transformChanged) {
159 AddRegion(result, NewTransformedBounds());
160 }
162 // If we don't have to generate invalidations separately for child
163 // layers then we can just stop here since we've already invalidated the entire
164 // old and new bounds.
165 if (!aCallback) {
166 ClearInvalidations(mLayer);
167 return result;
168 }
169 }
171 nsIntRegion visible;
172 visible.Xor(mVisibleRegion, mLayer->GetVisibleRegion());
173 if (!visible.IsEmpty()) {
174 aGeometryChanged = true;
175 }
176 AddTransformedRegion(result, visible, mTransform);
178 AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
179 AddTransformedRegion(result, mLayer->GetInvalidRegion(), mTransform);
181 if (mMaskLayer && otherMask) {
182 AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
183 mTransform);
184 }
186 if (mUseClipRect && otherClip) {
187 if (!mClipRect.IsEqualInterior(*otherClip)) {
188 nsIntRegion tmp;
189 tmp.Xor(mClipRect, *otherClip);
190 AddRegion(result, tmp);
191 }
192 }
194 mLayer->ClearInvalidRect();
195 return result;
196 }
198 nsIntRect NewTransformedBounds()
199 {
200 gfx3DMatrix transform;
201 gfx::To3DMatrix(mLayer->GetTransform(), transform);
202 return TransformRect(mLayer->GetVisibleRegion().GetBounds(), transform);
203 }
205 nsIntRect OldTransformedBounds()
206 {
207 return TransformRect(mVisibleRegion.GetBounds(), mTransform);
208 }
210 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
211 bool& aGeometryChanged)
212 {
213 return nsIntRect();
214 }
216 nsRefPtr<Layer> mLayer;
217 nsAutoPtr<LayerPropertiesBase> mMaskLayer;
218 nsIntRegion mVisibleRegion;
219 nsIntRegion mInvalidRegion;
220 gfx3DMatrix mTransform;
221 float mOpacity;
222 nsIntRect mClipRect;
223 bool mUseClipRect;
224 };
226 struct ContainerLayerProperties : public LayerPropertiesBase
227 {
228 ContainerLayerProperties(ContainerLayer* aLayer)
229 : LayerPropertiesBase(aLayer)
230 {
231 for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
232 mChildren.AppendElement(CloneLayerTreePropertiesInternal(child));
233 }
234 }
236 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
237 bool& aGeometryChanged)
238 {
239 ContainerLayer* container = mLayer->AsContainerLayer();
240 nsIntRegion result;
242 // A low frame rate is especially visible to users when scrolling, so we
243 // particularly want to avoid unnecessary invalidation at that time. For us
244 // here, that means avoiding unnecessary invalidation of child items when
245 // other children are added to or removed from our container layer, since
246 // that may be caused by children being scrolled in or out of view. We are
247 // less concerned with children changing order.
248 // TODO: Consider how we could avoid unnecessary invalidation when children
249 // change order, and whether the overhead would be worth it.
251 nsDataHashtable<nsPtrHashKey<Layer>, uint32_t> oldIndexMap(mChildren.Length());
252 for (uint32_t i = 0; i < mChildren.Length(); ++i) {
253 oldIndexMap.Put(mChildren[i]->mLayer, i);
254 }
256 uint32_t i = 0; // cursor into the old child list mChildren
257 for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
258 bool invalidateChildsCurrentArea = false;
259 if (i < mChildren.Length()) {
260 uint32_t childsOldIndex;
261 if (oldIndexMap.Get(child, &childsOldIndex)) {
262 if (childsOldIndex >= i) {
263 // Invalidate the old areas of layers that used to be between the
264 // current |child| and the previous |child| that was also in the
265 // old list mChildren (if any of those children have been reordered
266 // rather than removed, we will invalidate their new area when we
267 // encounter them in the new list):
268 for (uint32_t j = i; j < childsOldIndex; ++j) {
269 AddRegion(result, mChildren[j]->OldTransformedBounds());
270 }
271 // Invalidate any regions of the child that have changed:
272 AddRegion(result, mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged));
273 i = childsOldIndex + 1;
274 } else {
275 // We've already seen this child in mChildren (which means it must
276 // have been reordered) and invalidated its old area. We need to
277 // invalidate its new area too:
278 invalidateChildsCurrentArea = true;
279 }
280 } else {
281 // |child| is new
282 invalidateChildsCurrentArea = true;
283 }
284 } else {
285 // |child| is new, or was reordered to a higher index
286 invalidateChildsCurrentArea = true;
287 }
288 if (invalidateChildsCurrentArea) {
289 gfx3DMatrix transform;
290 gfx::To3DMatrix(child->GetTransform(), transform);
291 AddTransformedRegion(result, child->GetVisibleRegion(), transform);
292 if (aCallback) {
293 NotifySubdocumentInvalidationRecursive(child, aCallback);
294 } else {
295 ClearInvalidations(child);
296 }
297 }
298 }
300 // Process remaining removed children.
301 while (i < mChildren.Length()) {
302 AddRegion(result, mChildren[i]->OldTransformedBounds());
303 i++;
304 }
306 if (aCallback) {
307 aCallback(container, result);
308 }
310 gfx3DMatrix transform;
311 gfx::To3DMatrix(mLayer->GetTransform(), transform);
312 return TransformRegion(result, transform);
313 }
315 // The old list of children:
316 nsAutoTArray<nsAutoPtr<LayerPropertiesBase>,1> mChildren;
317 };
319 struct ColorLayerProperties : public LayerPropertiesBase
320 {
321 ColorLayerProperties(ColorLayer *aLayer)
322 : LayerPropertiesBase(aLayer)
323 , mColor(aLayer->GetColor())
324 { }
326 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
327 bool& aGeometryChanged)
328 {
329 ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
331 if (mColor != color->GetColor()) {
332 return NewTransformedBounds();
333 }
335 return nsIntRegion();
336 }
338 gfxRGBA mColor;
339 };
341 struct ImageLayerProperties : public LayerPropertiesBase
342 {
343 ImageLayerProperties(ImageLayer* aImage)
344 : LayerPropertiesBase(aImage)
345 , mContainer(aImage->GetContainer())
346 , mFilter(aImage->GetFilter())
347 , mScaleToSize(aImage->GetScaleToSize())
348 , mScaleMode(aImage->GetScaleMode())
349 {
350 }
352 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
353 bool& aGeometryChanged)
354 {
355 ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
357 if (!imageLayer->GetVisibleRegion().IsEqual(mVisibleRegion)) {
358 nsIntRect result = NewTransformedBounds();
359 result = result.Union(OldTransformedBounds());
360 return result;
361 }
363 if (mContainer != imageLayer->GetContainer() ||
364 mFilter != imageLayer->GetFilter() ||
365 mScaleToSize != imageLayer->GetScaleToSize() ||
366 mScaleMode != imageLayer->GetScaleMode()) {
367 return NewTransformedBounds();
368 }
370 return nsIntRect();
371 }
373 nsRefPtr<ImageContainer> mContainer;
374 GraphicsFilter mFilter;
375 gfx::IntSize mScaleToSize;
376 ScaleMode mScaleMode;
377 };
379 LayerPropertiesBase*
380 CloneLayerTreePropertiesInternal(Layer* aRoot)
381 {
382 if (!aRoot) {
383 return new LayerPropertiesBase();
384 }
386 switch (aRoot->GetType()) {
387 case Layer::TYPE_CONTAINER:
388 case Layer::TYPE_REF:
389 return new ContainerLayerProperties(aRoot->AsContainerLayer());
390 case Layer::TYPE_COLOR:
391 return new ColorLayerProperties(static_cast<ColorLayer*>(aRoot));
392 case Layer::TYPE_IMAGE:
393 return new ImageLayerProperties(static_cast<ImageLayer*>(aRoot));
394 default:
395 return new LayerPropertiesBase(aRoot);
396 }
398 return nullptr;
399 }
401 /* static */ LayerProperties*
402 LayerProperties::CloneFrom(Layer* aRoot)
403 {
404 return CloneLayerTreePropertiesInternal(aRoot);
405 }
407 /* static */ void
408 LayerProperties::ClearInvalidations(Layer *aLayer)
409 {
410 aLayer->ClearInvalidRect();
411 if (aLayer->GetMaskLayer()) {
412 ClearInvalidations(aLayer->GetMaskLayer());
413 }
415 ContainerLayer* container = aLayer->AsContainerLayer();
416 if (!container) {
417 return;
418 }
420 for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) {
421 ClearInvalidations(child);
422 }
423 }
425 nsIntRegion
426 LayerPropertiesBase::ComputeDifferences(Layer* aRoot, NotifySubDocInvalidationFunc aCallback,
427 bool* aGeometryChanged = nullptr)
428 {
429 NS_ASSERTION(aRoot, "Must have a layer tree to compare against!");
430 if (mLayer != aRoot) {
431 if (aCallback) {
432 NotifySubdocumentInvalidationRecursive(aRoot, aCallback);
433 } else {
434 ClearInvalidations(aRoot);
435 }
436 gfx3DMatrix transform;
437 gfx::To3DMatrix(aRoot->GetTransform(), transform);
438 nsIntRect result = TransformRect(aRoot->GetVisibleRegion().GetBounds(), transform);
439 result = result.Union(OldTransformedBounds());
440 if (aGeometryChanged != nullptr) {
441 *aGeometryChanged = true;
442 }
443 return result;
444 } else {
445 bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
446 nsIntRegion invalid = ComputeChange(aCallback, geometryChanged);
447 if (aGeometryChanged != nullptr) {
448 *aGeometryChanged = geometryChanged;
449 }
450 return invalid;
451 }
452 }
454 void
455 LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset)
456 {
457 mTransform.TranslatePost(gfxPoint3D(aOffset.x, aOffset.y, 0));
458 }
460 } // namespace layers
461 } // namespace mozilla