StackingContextHelper.cpp (11710B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/layers/StackingContextHelper.h" 8 9 #include "mozilla/PresShell.h" 10 #include "mozilla/gfx/Point.h" 11 #include "mozilla/gfx/Matrix.h" 12 #include "UnitTransforms.h" 13 #include "nsDisplayList.h" 14 #include "mozilla/dom/BrowserChild.h" 15 #include "nsLayoutUtils.h" 16 #include "ActiveLayerTracker.h" 17 18 namespace mozilla { 19 namespace layers { 20 using namespace gfx; 21 22 StackingContextHelper::StackingContextHelper() 23 : mBuilder(nullptr), 24 mScale(1.0f, 1.0f), 25 mAffectsClipPositioning(false), 26 mDeferredTransformItem(nullptr), 27 mRasterizeLocally(false) {} 28 29 static nsSize ComputeDesiredDisplaySizeForAnimation(nsIFrame* aContainerFrame) { 30 // Use the size of the nearest widget as the maximum size. This 31 // is important since it might be a popup that is bigger than the 32 // pres context's size. 33 nsPresContext* presContext = aContainerFrame->PresContext(); 34 nsIWidget* widget = aContainerFrame->GetNearestWidget(); 35 if (widget) { 36 return LayoutDevicePixel::ToAppUnits(widget->GetClientSize(), 37 presContext->AppUnitsPerDevPixel()); 38 } 39 40 return presContext->GetVisibleArea().Size(); 41 } 42 43 /* static */ 44 MatrixScales ChooseScale(nsIFrame* aContainerFrame, 45 nsDisplayItem* aContainerItem, 46 const nsRect& aVisibleRect, float aXScale, 47 float aYScale, const Matrix& aTransform2d, 48 bool aCanDraw2D) { 49 MatrixScales scale; 50 // XXX Should we do something for 3D transforms? 51 if (aCanDraw2D && !aContainerFrame->Combines3DTransformWithAncestors() && 52 !aContainerFrame->HasPerspective()) { 53 // If the container's transform is animated off main thread, fix a suitable 54 // scale size for animation 55 if (aContainerItem && 56 aContainerItem->GetType() == DisplayItemType::TYPE_TRANSFORM && 57 // FIXME: What we need is only transform, rotate, and scale, not 58 // translate, so it's be better to use a property set, instead of 59 // display item type here. 60 EffectCompositor::HasAnimationsForCompositor( 61 aContainerFrame, DisplayItemType::TYPE_TRANSFORM)) { 62 nsSize displaySize = 63 ComputeDesiredDisplaySizeForAnimation(aContainerFrame); 64 // compute scale using the animation on the container, taking ancestors in 65 // to account 66 nsSize scaledVisibleSize = nsSize(aVisibleRect.Width() * aXScale, 67 aVisibleRect.Height() * aYScale); 68 scale = nsLayoutUtils::ComputeSuitableScaleForAnimation( 69 aContainerFrame, scaledVisibleSize, displaySize); 70 // multiply by the scale inherited from ancestors--we use a uniform 71 // scale factor to prevent blurring when the layer is rotated. 72 float incomingScale = std::max(aXScale, aYScale); 73 scale = scale * ScaleFactor<UnknownUnits, UnknownUnits>(incomingScale); 74 } else { 75 // Scale factors are normalized to a power of 2 to reduce the number of 76 // resolution changes 77 scale = (aTransform2d * gfx::Matrix::Scaling(aXScale, aYScale)) 78 .ScaleFactors(); 79 // For frames with a changing scale transform round scale factors up to 80 // nearest power-of-2 boundary so that we don't keep having to redraw 81 // the content as it scales up and down. Rounding up to nearest 82 // power-of-2 boundary ensures we never scale up, only down --- avoiding 83 // jaggies. It also ensures we never scale down by more than a factor of 84 // 2, avoiding bad downscaling quality. 85 Matrix frameTransform; 86 if (ActiveLayerTracker::IsScaleSubjectToAnimation(aContainerFrame)) { 87 scale.xScale = gfxUtils::ClampToScaleFactor(scale.xScale); 88 scale.yScale = gfxUtils::ClampToScaleFactor(scale.yScale); 89 90 // Limit animated scale factors to not grow excessively beyond the 91 // display size. 92 nsSize maxScale(4, 4); 93 if (!aVisibleRect.IsEmpty()) { 94 nsSize displaySize = 95 ComputeDesiredDisplaySizeForAnimation(aContainerFrame); 96 maxScale = Max(maxScale, displaySize / aVisibleRect.Size()); 97 } 98 if (scale.xScale > maxScale.width) { 99 scale.xScale = gfxUtils::ClampToScaleFactor(maxScale.width, true); 100 } 101 if (scale.yScale > maxScale.height) { 102 scale.yScale = gfxUtils::ClampToScaleFactor(maxScale.height, true); 103 } 104 } else { 105 // XXX Do we need to move nearly-integer values to integers here? 106 } 107 } 108 // If the scale factors are too small, just use 1.0. The content is being 109 // scaled out of sight anyway. 110 if (fabs(scale.xScale) < 1e-8 || fabs(scale.yScale) < 1e-8) { 111 scale = MatrixScales(1.0, 1.0); 112 } 113 } else { 114 scale = MatrixScales(1.0, 1.0); 115 } 116 117 // Prevent the scale from getting too large, to avoid excessive memory 118 // allocation. Usually memory allocation is limited by the visible region, 119 // which should be restricted to the display port. But at very large scales 120 // the visible region itself can become excessive due to rounding errors. 121 // Clamping the scale here prevents that. 122 return MatrixScales(std::min(scale.xScale, 32768.0f), 123 std::min(scale.yScale, 32768.0f)); 124 } 125 126 StackingContextHelper::StackingContextHelper( 127 const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr, 128 nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem, 129 wr::DisplayListBuilder& aBuilder, const wr::StackingContextParams& aParams, 130 const LayoutDeviceRect& aBounds) 131 : mBuilder(&aBuilder), 132 mScale(1.0f, 1.0f), 133 mDeferredTransformItem(aParams.mDeferredTransformItem), 134 mRasterizeLocally(aParams.mRasterizeLocally || 135 aParentSC.mRasterizeLocally) { 136 MOZ_ASSERT(!aContainerItem || aContainerItem->CreatesStackingContextHelper()); 137 138 // Compute scale for fallback rendering. We don't try to guess a scale for 3d 139 // transformed items 140 141 if (aParams.mBoundTransform) { 142 gfx::Matrix transform2d; 143 bool canDraw2D = aParams.mBoundTransform->CanDraw2D(&transform2d); 144 if (canDraw2D && 145 aParams.reference_frame_kind != wr::WrReferenceFrameKind::Perspective && 146 !aContainerFrame->Combines3DTransformWithAncestors()) { 147 mInheritedTransform = transform2d * aParentSC.mInheritedTransform; 148 149 int32_t apd = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); 150 nsRect r = LayoutDevicePixel::ToAppUnits(aBounds, apd); 151 mScale = ChooseScale(aContainerFrame, aContainerItem, r, 152 aParentSC.mScale.xScale, aParentSC.mScale.yScale, 153 transform2d, 154 /* aCanDraw2D = */ true); 155 } else { 156 mScale = gfx::MatrixScales(1.0f, 1.0f); 157 mInheritedTransform = gfx::Matrix::Scaling(1.f, 1.f); 158 } 159 160 if (aParams.mAnimated) { 161 mSnappingSurfaceTransform = gfx::Matrix::Scaling(mScale); 162 } else { 163 mSnappingSurfaceTransform = 164 transform2d * aParentSC.mSnappingSurfaceTransform; 165 } 166 167 } else if (aParams.reference_frame_kind == 168 wr::WrReferenceFrameKind::Transform && 169 aContainerItem && 170 aContainerItem->GetType() == DisplayItemType::TYPE_ASYNC_ZOOM && 171 aContainerItem->Frame()) { 172 float resolution = aContainerItem->Frame()->PresShell()->GetResolution(); 173 gfx::Matrix transform = gfx::Matrix::Scaling(resolution, resolution); 174 175 mInheritedTransform = transform * aParentSC.mInheritedTransform; 176 mScale = 177 ScaleFactor<UnknownUnits, UnknownUnits>(resolution) * aParentSC.mScale; 178 179 MOZ_ASSERT(!aParams.mAnimated); 180 mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform; 181 182 } else if (!aAsr && !aContainerFrame && !aContainerItem && 183 aParams.mRootReferenceFrame) { 184 // this is the root stacking context helper 185 Scale2D resolution; 186 187 // If we are in a remote browser, then apply scaling from ancestor browsers 188 if (mozilla::dom::BrowserChild* browserChild = 189 mozilla::dom::BrowserChild::GetFrom( 190 aParams.mRootReferenceFrame->PresShell())) { 191 resolution = browserChild->GetEffectsInfo().mRasterScale; 192 } 193 194 gfx::Matrix transform = 195 gfx::Matrix::Scaling(resolution.xScale, resolution.yScale); 196 197 mInheritedTransform = transform * aParentSC.mInheritedTransform; 198 mScale = aParentSC.mScale * resolution; 199 200 MOZ_ASSERT(!aParams.mAnimated); 201 mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform; 202 203 } else { 204 mInheritedTransform = aParentSC.mInheritedTransform; 205 mScale = aParentSC.mScale; 206 } 207 208 auto rasterSpace = 209 mRasterizeLocally 210 ? wr::RasterSpace::Local(std::max(mScale.xScale, mScale.yScale)) 211 : wr::RasterSpace::Screen(); 212 213 MOZ_ASSERT(!aParams.clip.IsNone()); 214 mReferenceFrameId = mBuilder->PushStackingContext( 215 aParams, wr::ToLayoutRect(aBounds), rasterSpace); 216 217 if (mReferenceFrameId) { 218 mSpaceAndClipChainHelper.emplace(aBuilder, mReferenceFrameId.ref()); 219 } 220 221 mAffectsClipPositioning = 222 mReferenceFrameId.isSome() || (aBounds.TopLeft() != LayoutDevicePoint()); 223 224 // If the parent stacking context has a deferred transform item, inherit it 225 // into this stacking context, as long as the ASR hasn't changed. Refer to 226 // the comments on StackingContextHelper::mDeferredTransformItem for an 227 // explanation of what goes in these fields. 228 if (aParentSC.mDeferredTransformItem && 229 aAsr == aParentSC.mDeferredTransformItem->GetActiveScrolledRoot()) { 230 if (mDeferredTransformItem) { 231 // If we are deferring another transform, put the combined transform from 232 // all the ancestor deferred items into mDeferredAncestorTransform 233 mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix(); 234 } else { 235 // We are not deferring another transform, so we can just inherit the 236 // parent stacking context's deferred data without any modification. 237 mDeferredTransformItem = aParentSC.mDeferredTransformItem; 238 mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform; 239 } 240 } 241 } 242 243 StackingContextHelper::~StackingContextHelper() { 244 if (mBuilder) { 245 mSpaceAndClipChainHelper.reset(); 246 mBuilder->PopStackingContext(mReferenceFrameId.isSome()); 247 } 248 } 249 250 nsDisplayTransform* StackingContextHelper::GetDeferredTransformItem() const { 251 return mDeferredTransformItem; 252 } 253 254 Maybe<gfx::Matrix4x4> StackingContextHelper::GetDeferredTransformMatrix() 255 const { 256 if (mDeferredTransformItem) { 257 // See the comments on StackingContextHelper::mDeferredTransformItem for 258 // an explanation of what's stored in mDeferredTransformItem and 259 // mDeferredAncestorTransform. Here we need to return the combined transform 260 // transform from all the deferred ancestors, including 261 // mDeferredTransformItem. 262 gfx::Matrix4x4 result = mDeferredTransformItem->GetTransform().GetMatrix(); 263 if (mDeferredAncestorTransform) { 264 result = result * *mDeferredAncestorTransform; 265 } 266 return Some(result); 267 } else { 268 return Nothing(); 269 } 270 } 271 272 void StackingContextHelper::ClearDeferredTransformItem() const { 273 mDeferredTransformItem = nullptr; 274 } 275 276 void StackingContextHelper::RestoreDeferredTransformItem( 277 nsDisplayTransform* aItem) const { 278 mDeferredTransformItem = aItem; 279 } 280 281 } // namespace layers 282 } // namespace mozilla