layout/base/FrameLayerBuilder.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

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

mercurial