|
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/. */ |
|
5 |
|
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 |
|
28 |
|
29 namespace mozilla { |
|
30 namespace layers { |
|
31 |
|
32 struct LayerPropertiesBase; |
|
33 LayerPropertiesBase* CloneLayerTreePropertiesInternal(Layer* aRoot); |
|
34 |
|
35 static nsIntRect |
|
36 TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform) |
|
37 { |
|
38 if (aRect.IsEmpty()) { |
|
39 return nsIntRect(); |
|
40 } |
|
41 |
|
42 gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height); |
|
43 rect = aTransform.TransformBounds(rect); |
|
44 rect.RoundOut(); |
|
45 |
|
46 nsIntRect intRect; |
|
47 if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) { |
|
48 return nsIntRect(); |
|
49 } |
|
50 |
|
51 return intRect; |
|
52 } |
|
53 |
|
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 } |
|
64 |
|
65 static void |
|
66 AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) |
|
67 { |
|
68 aDest.Or(aDest, aSource); |
|
69 aDest.SimplifyOutward(20); |
|
70 } |
|
71 |
|
72 static nsIntRegion |
|
73 TransformRegion(const nsIntRegion& aRegion, const gfx3DMatrix& aTransform) |
|
74 { |
|
75 nsIntRegion result; |
|
76 AddTransformedRegion(result, aRegion, aTransform); |
|
77 return result; |
|
78 } |
|
79 |
|
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(); |
|
90 |
|
91 if (aLayer->GetMaskLayer()) { |
|
92 NotifySubdocumentInvalidationRecursive(aLayer->GetMaskLayer(), aCallback); |
|
93 } |
|
94 |
|
95 if (!container) { |
|
96 return; |
|
97 } |
|
98 |
|
99 for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { |
|
100 NotifySubdocumentInvalidationRecursive(child, aCallback); |
|
101 } |
|
102 |
|
103 aCallback(container, container->GetVisibleRegion()); |
|
104 } |
|
105 |
|
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 } |
|
135 |
|
136 virtual nsIntRegion ComputeDifferences(Layer* aRoot, |
|
137 NotifySubDocInvalidationFunc aCallback, |
|
138 bool* aGeometryChanged); |
|
139 |
|
140 virtual void MoveBy(const nsIntPoint& aOffset); |
|
141 |
|
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 } |
|
161 |
|
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 } |
|
170 |
|
171 nsIntRegion visible; |
|
172 visible.Xor(mVisibleRegion, mLayer->GetVisibleRegion()); |
|
173 if (!visible.IsEmpty()) { |
|
174 aGeometryChanged = true; |
|
175 } |
|
176 AddTransformedRegion(result, visible, mTransform); |
|
177 |
|
178 AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged)); |
|
179 AddTransformedRegion(result, mLayer->GetInvalidRegion(), mTransform); |
|
180 |
|
181 if (mMaskLayer && otherMask) { |
|
182 AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged), |
|
183 mTransform); |
|
184 } |
|
185 |
|
186 if (mUseClipRect && otherClip) { |
|
187 if (!mClipRect.IsEqualInterior(*otherClip)) { |
|
188 nsIntRegion tmp; |
|
189 tmp.Xor(mClipRect, *otherClip); |
|
190 AddRegion(result, tmp); |
|
191 } |
|
192 } |
|
193 |
|
194 mLayer->ClearInvalidRect(); |
|
195 return result; |
|
196 } |
|
197 |
|
198 nsIntRect NewTransformedBounds() |
|
199 { |
|
200 gfx3DMatrix transform; |
|
201 gfx::To3DMatrix(mLayer->GetTransform(), transform); |
|
202 return TransformRect(mLayer->GetVisibleRegion().GetBounds(), transform); |
|
203 } |
|
204 |
|
205 nsIntRect OldTransformedBounds() |
|
206 { |
|
207 return TransformRect(mVisibleRegion.GetBounds(), mTransform); |
|
208 } |
|
209 |
|
210 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, |
|
211 bool& aGeometryChanged) |
|
212 { |
|
213 return nsIntRect(); |
|
214 } |
|
215 |
|
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 }; |
|
225 |
|
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 } |
|
235 |
|
236 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, |
|
237 bool& aGeometryChanged) |
|
238 { |
|
239 ContainerLayer* container = mLayer->AsContainerLayer(); |
|
240 nsIntRegion result; |
|
241 |
|
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. |
|
250 |
|
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 } |
|
255 |
|
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 } |
|
299 |
|
300 // Process remaining removed children. |
|
301 while (i < mChildren.Length()) { |
|
302 AddRegion(result, mChildren[i]->OldTransformedBounds()); |
|
303 i++; |
|
304 } |
|
305 |
|
306 if (aCallback) { |
|
307 aCallback(container, result); |
|
308 } |
|
309 |
|
310 gfx3DMatrix transform; |
|
311 gfx::To3DMatrix(mLayer->GetTransform(), transform); |
|
312 return TransformRegion(result, transform); |
|
313 } |
|
314 |
|
315 // The old list of children: |
|
316 nsAutoTArray<nsAutoPtr<LayerPropertiesBase>,1> mChildren; |
|
317 }; |
|
318 |
|
319 struct ColorLayerProperties : public LayerPropertiesBase |
|
320 { |
|
321 ColorLayerProperties(ColorLayer *aLayer) |
|
322 : LayerPropertiesBase(aLayer) |
|
323 , mColor(aLayer->GetColor()) |
|
324 { } |
|
325 |
|
326 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, |
|
327 bool& aGeometryChanged) |
|
328 { |
|
329 ColorLayer* color = static_cast<ColorLayer*>(mLayer.get()); |
|
330 |
|
331 if (mColor != color->GetColor()) { |
|
332 return NewTransformedBounds(); |
|
333 } |
|
334 |
|
335 return nsIntRegion(); |
|
336 } |
|
337 |
|
338 gfxRGBA mColor; |
|
339 }; |
|
340 |
|
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 } |
|
351 |
|
352 virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, |
|
353 bool& aGeometryChanged) |
|
354 { |
|
355 ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get()); |
|
356 |
|
357 if (!imageLayer->GetVisibleRegion().IsEqual(mVisibleRegion)) { |
|
358 nsIntRect result = NewTransformedBounds(); |
|
359 result = result.Union(OldTransformedBounds()); |
|
360 return result; |
|
361 } |
|
362 |
|
363 if (mContainer != imageLayer->GetContainer() || |
|
364 mFilter != imageLayer->GetFilter() || |
|
365 mScaleToSize != imageLayer->GetScaleToSize() || |
|
366 mScaleMode != imageLayer->GetScaleMode()) { |
|
367 return NewTransformedBounds(); |
|
368 } |
|
369 |
|
370 return nsIntRect(); |
|
371 } |
|
372 |
|
373 nsRefPtr<ImageContainer> mContainer; |
|
374 GraphicsFilter mFilter; |
|
375 gfx::IntSize mScaleToSize; |
|
376 ScaleMode mScaleMode; |
|
377 }; |
|
378 |
|
379 LayerPropertiesBase* |
|
380 CloneLayerTreePropertiesInternal(Layer* aRoot) |
|
381 { |
|
382 if (!aRoot) { |
|
383 return new LayerPropertiesBase(); |
|
384 } |
|
385 |
|
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 } |
|
397 |
|
398 return nullptr; |
|
399 } |
|
400 |
|
401 /* static */ LayerProperties* |
|
402 LayerProperties::CloneFrom(Layer* aRoot) |
|
403 { |
|
404 return CloneLayerTreePropertiesInternal(aRoot); |
|
405 } |
|
406 |
|
407 /* static */ void |
|
408 LayerProperties::ClearInvalidations(Layer *aLayer) |
|
409 { |
|
410 aLayer->ClearInvalidRect(); |
|
411 if (aLayer->GetMaskLayer()) { |
|
412 ClearInvalidations(aLayer->GetMaskLayer()); |
|
413 } |
|
414 |
|
415 ContainerLayer* container = aLayer->AsContainerLayer(); |
|
416 if (!container) { |
|
417 return; |
|
418 } |
|
419 |
|
420 for (Layer* child = container->GetFirstChild(); child; child = child->GetNextSibling()) { |
|
421 ClearInvalidations(child); |
|
422 } |
|
423 } |
|
424 |
|
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 } |
|
453 |
|
454 void |
|
455 LayerPropertiesBase::MoveBy(const nsIntPoint& aOffset) |
|
456 { |
|
457 mTransform.TranslatePost(gfxPoint3D(aOffset.x, aOffset.y, 0)); |
|
458 } |
|
459 |
|
460 } // namespace layers |
|
461 } // namespace mozilla |