diff options
Diffstat (limited to 'gfx/layers/wr/StackingContextHelper.cpp')
-rw-r--r-- | gfx/layers/wr/StackingContextHelper.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/gfx/layers/wr/StackingContextHelper.cpp b/gfx/layers/wr/StackingContextHelper.cpp new file mode 100644 index 0000000000..2e0ff1e0c1 --- /dev/null +++ b/gfx/layers/wr/StackingContextHelper.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/layers/StackingContextHelper.h" + +#include "mozilla/PresShell.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/Matrix.h" +#include "UnitTransforms.h" +#include "nsDisplayList.h" +#include "mozilla/dom/BrowserChild.h" +#include "nsLayoutUtils.h" +#include "ActiveLayerTracker.h" + +namespace mozilla { +namespace layers { +using namespace gfx; + +StackingContextHelper::StackingContextHelper() + : mBuilder(nullptr), + mScale(1.0f, 1.0f), + mAffectsClipPositioning(false), + mDeferredTransformItem(nullptr), + mRasterizeLocally(false) { + // mOrigin remains at 0,0 +} + +static nsSize ComputeDesiredDisplaySizeForAnimation(nsIFrame* aContainerFrame) { + // Use the size of the nearest widget as the maximum size. This + // is important since it might be a popup that is bigger than the + // pres context's size. + nsPresContext* presContext = aContainerFrame->PresContext(); + nsIWidget* widget = aContainerFrame->GetNearestWidget(); + if (widget) { + return LayoutDevicePixel::ToAppUnits(widget->GetClientSize(), + presContext->AppUnitsPerDevPixel()); + } + + return presContext->GetVisibleArea().Size(); +} + +/* static */ +MatrixScales ChooseScale(nsIFrame* aContainerFrame, + nsDisplayItem* aContainerItem, + const nsRect& aVisibleRect, float aXScale, + float aYScale, const Matrix& aTransform2d, + bool aCanDraw2D) { + MatrixScales scale; + // XXX Should we do something for 3D transforms? + if (aCanDraw2D && !aContainerFrame->Combines3DTransformWithAncestors() && + !aContainerFrame->HasPerspective()) { + // If the container's transform is animated off main thread, fix a suitable + // scale size for animation + if (aContainerItem && + aContainerItem->GetType() == DisplayItemType::TYPE_TRANSFORM && + // FIXME: What we need is only transform, rotate, and scale, not + // translate, so it's be better to use a property set, instead of + // display item type here. + EffectCompositor::HasAnimationsForCompositor( + aContainerFrame, DisplayItemType::TYPE_TRANSFORM)) { + nsSize displaySize = + ComputeDesiredDisplaySizeForAnimation(aContainerFrame); + // compute scale using the animation on the container, taking ancestors in + // to account + nsSize scaledVisibleSize = nsSize(aVisibleRect.Width() * aXScale, + aVisibleRect.Height() * aYScale); + scale = nsLayoutUtils::ComputeSuitableScaleForAnimation( + aContainerFrame, scaledVisibleSize, displaySize); + // multiply by the scale inherited from ancestors--we use a uniform + // scale factor to prevent blurring when the layer is rotated. + float incomingScale = std::max(aXScale, aYScale); + scale = scale * ScaleFactor<UnknownUnits, UnknownUnits>(incomingScale); + } else { + // Scale factors are normalized to a power of 2 to reduce the number of + // resolution changes + scale = aTransform2d.ScaleFactors(); + // For frames with a changing scale transform round scale factors up to + // nearest power-of-2 boundary so that we don't keep having to redraw + // the content as it scales up and down. Rounding up to nearest + // power-of-2 boundary ensures we never scale up, only down --- avoiding + // jaggies. It also ensures we never scale down by more than a factor of + // 2, avoiding bad downscaling quality. + Matrix frameTransform; + if (ActiveLayerTracker::IsScaleSubjectToAnimation(aContainerFrame)) { + scale.xScale = gfxUtils::ClampToScaleFactor(scale.xScale); + scale.yScale = gfxUtils::ClampToScaleFactor(scale.yScale); + + // Limit animated scale factors to not grow excessively beyond the + // display size. + nsSize maxScale(4, 4); + if (!aVisibleRect.IsEmpty()) { + nsSize displaySize = + ComputeDesiredDisplaySizeForAnimation(aContainerFrame); + maxScale = Max(maxScale, displaySize / aVisibleRect.Size()); + } + if (scale.xScale > maxScale.width) { + scale.xScale = gfxUtils::ClampToScaleFactor(maxScale.width, true); + } + if (scale.yScale > maxScale.height) { + scale.yScale = gfxUtils::ClampToScaleFactor(maxScale.height, true); + } + } else { + // XXX Do we need to move nearly-integer values to integers here? + } + } + // If the scale factors are too small, just use 1.0. The content is being + // scaled out of sight anyway. + if (fabs(scale.xScale) < 1e-8 || fabs(scale.yScale) < 1e-8) { + scale = MatrixScales(1.0, 1.0); + } + } else { + scale = MatrixScales(1.0, 1.0); + } + + // Prevent the scale from getting too large, to avoid excessive memory + // allocation. Usually memory allocation is limited by the visible region, + // which should be restricted to the display port. But at very large scales + // the visible region itself can become excessive due to rounding errors. + // Clamping the scale here prevents that. + return MatrixScales(std::min(scale.xScale, 32768.0f), + std::min(scale.yScale, 32768.0f)); +} + +StackingContextHelper::StackingContextHelper( + const StackingContextHelper& aParentSC, const ActiveScrolledRoot* aAsr, + nsIFrame* aContainerFrame, nsDisplayItem* aContainerItem, + wr::DisplayListBuilder& aBuilder, const wr::StackingContextParams& aParams, + const LayoutDeviceRect& aBounds) + : mBuilder(&aBuilder), + mScale(1.0f, 1.0f), + mDeferredTransformItem(aParams.mDeferredTransformItem), + mRasterizeLocally(aParams.mRasterizeLocally || + aParentSC.mRasterizeLocally) { + MOZ_ASSERT(!aContainerItem || aContainerItem->CreatesStackingContextHelper()); + + mOrigin = aParentSC.mOrigin + aBounds.TopLeft(); + // Compute scale for fallback rendering. We don't try to guess a scale for 3d + // transformed items + + if (aParams.mBoundTransform) { + gfx::Matrix transform2d; + bool canDraw2D = aParams.mBoundTransform->CanDraw2D(&transform2d); + if (canDraw2D && + aParams.reference_frame_kind != wr::WrReferenceFrameKind::Perspective && + !aContainerFrame->Combines3DTransformWithAncestors()) { + mInheritedTransform = transform2d * aParentSC.mInheritedTransform; + + int32_t apd = aContainerFrame->PresContext()->AppUnitsPerDevPixel(); + nsRect r = LayoutDevicePixel::ToAppUnits(aBounds, apd); + mScale = ChooseScale(aContainerFrame, aContainerItem, r, + aParentSC.mScale.xScale, aParentSC.mScale.yScale, + mInheritedTransform, + /* aCanDraw2D = */ true); + } else { + mScale = gfx::MatrixScales(1.0f, 1.0f); + mInheritedTransform = gfx::Matrix::Scaling(1.f, 1.f); + } + + if (aParams.mAnimated) { + mSnappingSurfaceTransform = gfx::Matrix::Scaling(mScale); + } else { + mSnappingSurfaceTransform = + transform2d * aParentSC.mSnappingSurfaceTransform; + } + + } else if (aParams.reference_frame_kind == + wr::WrReferenceFrameKind::Transform && + aContainerItem && + aContainerItem->GetType() == DisplayItemType::TYPE_ASYNC_ZOOM && + aContainerItem->Frame()) { + float resolution = aContainerItem->Frame()->PresShell()->GetResolution(); + gfx::Matrix transform = gfx::Matrix::Scaling(resolution, resolution); + + mInheritedTransform = transform * aParentSC.mInheritedTransform; + mScale = + ScaleFactor<UnknownUnits, UnknownUnits>(resolution) * aParentSC.mScale; + + MOZ_ASSERT(!aParams.mAnimated); + mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform; + + } else if (!aAsr && !aContainerFrame && !aContainerItem && + aParams.mRootReferenceFrame) { + // this is the root stacking context helper + Scale2D resolution; + + // If we are in a remote browser, then apply scaling from ancestor browsers + if (mozilla::dom::BrowserChild* browserChild = + mozilla::dom::BrowserChild::GetFrom( + aParams.mRootReferenceFrame->PresShell())) { + resolution = browserChild->GetEffectsInfo().mRasterScale; + } + + gfx::Matrix transform = + gfx::Matrix::Scaling(resolution.xScale, resolution.yScale); + + mInheritedTransform = transform * aParentSC.mInheritedTransform; + mScale = aParentSC.mScale * resolution; + + MOZ_ASSERT(!aParams.mAnimated); + mSnappingSurfaceTransform = transform * aParentSC.mSnappingSurfaceTransform; + + } else { + mInheritedTransform = aParentSC.mInheritedTransform; + mScale = aParentSC.mScale; + } + + auto rasterSpace = + mRasterizeLocally + ? wr::RasterSpace::Local(std::max(mScale.xScale, mScale.yScale)) + : wr::RasterSpace::Screen(); + + MOZ_ASSERT(!aParams.clip.IsNone()); + mReferenceFrameId = mBuilder->PushStackingContext( + aParams, wr::ToLayoutRect(aBounds), rasterSpace); + + if (mReferenceFrameId) { + mSpaceAndClipChainHelper.emplace(aBuilder, mReferenceFrameId.ref()); + } + + mAffectsClipPositioning = + mReferenceFrameId.isSome() || (aBounds.TopLeft() != LayoutDevicePoint()); + + // If the parent stacking context has a deferred transform item, inherit it + // into this stacking context, as long as the ASR hasn't changed. Refer to + // the comments on StackingContextHelper::mDeferredTransformItem for an + // explanation of what goes in these fields. + if (aParentSC.mDeferredTransformItem && + aAsr == aParentSC.mDeferredTransformItem->GetActiveScrolledRoot()) { + if (mDeferredTransformItem) { + // If we are deferring another transform, put the combined transform from + // all the ancestor deferred items into mDeferredAncestorTransform + mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix(); + } else { + // We are not deferring another transform, so we can just inherit the + // parent stacking context's deferred data without any modification. + mDeferredTransformItem = aParentSC.mDeferredTransformItem; + mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform; + } + } +} + +StackingContextHelper::~StackingContextHelper() { + if (mBuilder) { + mSpaceAndClipChainHelper.reset(); + mBuilder->PopStackingContext(mReferenceFrameId.isSome()); + } +} + +nsDisplayTransform* StackingContextHelper::GetDeferredTransformItem() const { + return mDeferredTransformItem; +} + +Maybe<gfx::Matrix4x4> StackingContextHelper::GetDeferredTransformMatrix() + const { + if (mDeferredTransformItem) { + // See the comments on StackingContextHelper::mDeferredTransformItem for + // an explanation of what's stored in mDeferredTransformItem and + // mDeferredAncestorTransform. Here we need to return the combined transform + // transform from all the deferred ancestors, including + // mDeferredTransformItem. + gfx::Matrix4x4 result = mDeferredTransformItem->GetTransform().GetMatrix(); + if (mDeferredAncestorTransform) { + result = result * *mDeferredAncestorTransform; + } + return Some(result); + } else { + return Nothing(); + } +} + +} // namespace layers +} // namespace mozilla |