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