summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/StackingContextHelper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/layers/wr/StackingContextHelper.cpp')
-rw-r--r--gfx/layers/wr/StackingContextHelper.cpp275
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