Bug 1364622 - Introduce a helper function for computing the async transform for a scroll thumb layer. r=kats
MozReview-Commit-ID: DFFcmBxzI84
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -1102,171 +1102,201 @@ LayerIsScrollbarTarget(const LayerMetric
return !aTarget.IsScrollInfoLayer();
}
static void
ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar,
const LayerMetricsWrapper& aContent,
bool aScrollbarIsDescendant)
{
+ AsyncTransformComponentMatrix clipTransform;
+
+ LayerToParentLayerMatrix4x4 transform =
+ AsyncCompositionManager::ComputeTransformForScrollThumb(
+ aScrollbar->GetLocalTransformTyped(),
+ aContent.GetTransform(),
+ aContent.GetApzc(),
+ aContent.Metrics(),
+ aScrollbar->GetScrollThumbData(),
+ aScrollbarIsDescendant,
+ &clipTransform);
+
+ if (aScrollbarIsDescendant) {
+ // We also need to make a corresponding change on the clip rect of all the
+ // layers on the ancestor chain from the scrollbar layer up to but not
+ // including the layer with the async transform. Otherwise the scrollbar
+ // shifts but gets clipped and so appears to flicker.
+ for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
+ TransformClipRect(ancestor, clipTransform);
+ }
+ }
+
+ SetShadowTransform(aScrollbar, transform);
+}
+
+/* static */ LayerToParentLayerMatrix4x4
+AsyncCompositionManager::ComputeTransformForScrollThumb(
+ const LayerToParentLayerMatrix4x4& aCurrentTransform,
+ const Matrix4x4& aScrollableContentTransform,
+ AsyncPanZoomController* aApzc,
+ const FrameMetrics& aMetrics,
+ const ScrollThumbData& aThumbData,
+ bool aScrollbarIsDescendant,
+ AsyncTransformComponentMatrix* aOutClipTransform)
+{
// We only apply the transform if the scroll-target layer has non-container
// children (i.e. when it has some possibly-visible content). This is to
// avoid moving scroll-bars in the situation that only a scroll information
// layer has been built for a scroll frame, as this would result in a
// disparity between scrollbars and visible content.
- if (aContent.IsScrollInfoLayer()) {
- return;
+ if (aMetrics.IsScrollInfoLayer()) {
+ return LayerToParentLayerMatrix4x4{};
}
- const FrameMetrics& metrics = aContent.Metrics();
- AsyncPanZoomController* apzc = aContent.GetApzc();
- MOZ_RELEASE_ASSERT(apzc);
+ MOZ_RELEASE_ASSERT(aApzc);
AsyncTransformComponentMatrix asyncTransform =
- apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ aApzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
// |asyncTransform| represents the amount by which we have scrolled and
// zoomed since the last paint. Because the scrollbar was sized and positioned based
// on the painted content, we need to adjust it based on asyncTransform so that
// it reflects what the user is actually seeing now.
AsyncTransformComponentMatrix scrollbarTransform;
- const ScrollThumbData& thumbData = aScrollbar->GetScrollThumbData();
- if (thumbData.mDirection == ScrollDirection::VERTICAL) {
+ if (aThumbData.mDirection == ScrollDirection::VERTICAL) {
const ParentLayerCoord asyncScrollY = asyncTransform._42;
const float asyncZoomY = asyncTransform._22;
// The scroll thumb needs to be scaled in the direction of scrolling by the
// inverse of the async zoom. This is because zooming in decreases the
// fraction of the whole srollable rect that is in view.
const float yScale = 1.f / asyncZoomY;
// Note: |metrics.GetZoom()| doesn't yet include the async zoom.
- const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().yScale * asyncZoomY);
+ const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().yScale * asyncZoomY);
// Here we convert the scrollbar thumb ratio into a true unitless ratio by
// dividing out the conversion factor from the scrollframe's parent's space
// to the scrollframe's space.
- const float ratio = thumbData.mThumbRatio /
- (metrics.GetPresShellResolution() * asyncZoomY);
+ const float ratio = aThumbData.mThumbRatio /
+ (aMetrics.GetPresShellResolution() * asyncZoomY);
// The scroll thumb needs to be translated in opposite direction of the
// async scroll. This is because scrolling down, which translates the layer
// content up, should result in moving the scroll thumb down.
ParentLayerCoord yTranslation = -asyncScrollY * ratio;
// The scroll thumb additionally needs to be translated to compensate for
// the scale applied above. The origin with respect to which the scale is
// applied is the origin of the entire scrollbar, rather than the origin of
// the scroll thumb (meaning, for a vertical scrollbar it's at the top of
// the composition bounds). This means that empty space above the thumb
// is scaled too, effectively translating the thumb. We undo that
// translation here.
// (One can think of the adjustment being done to the translation here as
// a change of basis. We have a method to help with that,
// Matrix4x4::ChangeBasis(), but it wouldn't necessarily make the code
// cleaner in this case).
- const CSSCoord thumbOrigin = (metrics.GetScrollOffset().y * ratio);
+ const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().y * ratio);
const CSSCoord thumbOriginScaled = thumbOrigin * yScale;
const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
yTranslation -= thumbOriginDeltaPL;
- if (metrics.IsRootContent()) {
+ if (aMetrics.IsRootContent()) {
// Scrollbar for the root are painted at the same resolution as the
// content. Since the coordinate space we apply this transform in includes
// the resolution, we need to adjust for it as well here. Note that in
// another metrics.IsRootContent() hunk below we apply a
// resolution-cancelling transform which ensures the scroll thumb isn't
// actually rendered at a larger scale.
- yTranslation *= metrics.GetPresShellResolution();
+ yTranslation *= aMetrics.GetPresShellResolution();
}
scrollbarTransform.PostScale(1.f, yScale, 1.f);
scrollbarTransform.PostTranslate(0, yTranslation, 0);
}
- if (thumbData.mDirection == ScrollDirection::HORIZONTAL) {
+ if (aThumbData.mDirection == ScrollDirection::HORIZONTAL) {
// See detailed comments under the VERTICAL case.
const ParentLayerCoord asyncScrollX = asyncTransform._41;
const float asyncZoomX = asyncTransform._11;
const float xScale = 1.f / asyncZoomX;
- const CSSToParentLayerScale effectiveZoom(metrics.GetZoom().xScale * asyncZoomX);
+ const CSSToParentLayerScale effectiveZoom(aMetrics.GetZoom().xScale * asyncZoomX);
- const float ratio = thumbData.mThumbRatio /
- (metrics.GetPresShellResolution() * asyncZoomX);
+ const float ratio = aThumbData.mThumbRatio /
+ (aMetrics.GetPresShellResolution() * asyncZoomX);
ParentLayerCoord xTranslation = -asyncScrollX * ratio;
- const CSSCoord thumbOrigin = (metrics.GetScrollOffset().x * ratio);
+ const CSSCoord thumbOrigin = (aMetrics.GetScrollOffset().x * ratio);
const CSSCoord thumbOriginScaled = thumbOrigin * xScale;
const CSSCoord thumbOriginDelta = thumbOriginScaled - thumbOrigin;
const ParentLayerCoord thumbOriginDeltaPL = thumbOriginDelta * effectiveZoom;
xTranslation -= thumbOriginDeltaPL;
- if (metrics.IsRootContent()) {
- xTranslation *= metrics.GetPresShellResolution();
+ if (aMetrics.IsRootContent()) {
+ xTranslation *= aMetrics.GetPresShellResolution();
}
scrollbarTransform.PostScale(xScale, 1.f, 1.f);
scrollbarTransform.PostTranslate(xTranslation, 0, 0);
}
LayerToParentLayerMatrix4x4 transform =
- aScrollbar->GetLocalTransformTyped() * scrollbarTransform;
+ aCurrentTransform * scrollbarTransform;
AsyncTransformComponentMatrix compensation;
// If the scrollbar layer is for the root then the content's resolution
// applies to the scrollbar as well. Since we don't actually want the scroll
// thumb's size to vary with the zoom (other than its length reflecting the
// fraction of the scrollable length that's in view, which is taken care of
// above), we apply a transform to cancel out this resolution.
- if (metrics.IsRootContent()) {
+ if (aMetrics.IsRootContent()) {
compensation =
AsyncTransformComponentMatrix::Scaling(
- metrics.GetPresShellResolution(),
- metrics.GetPresShellResolution(),
+ aMetrics.GetPresShellResolution(),
+ aMetrics.GetPresShellResolution(),
1.0f).Inverse();
}
// If the scrollbar layer is a child of the content it is a scrollbar for,
// then we need to adjust for any async transform (including an overscroll
// transform) on the content. This needs to be cancelled out because layout
// positions and sizes the scrollbar on the assumption that there is no async
// transform, and without this adjustment the scrollbar will end up in the
// wrong place.
//
// Note that since the async transform is applied on top of the content's
// regular transform, we need to make sure to unapply the async transform in
// the same coordinate space. This requires applying the content transform
// and then unapplying it after unapplying the async transform.
if (aScrollbarIsDescendant) {
AsyncTransformComponentMatrix overscroll =
- apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
+ aApzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE);
Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix();
- Matrix4x4 contentTransform = aContent.GetTransform();
+ Matrix4x4 contentTransform = aScrollableContentTransform;
Matrix4x4 contentUntransform = contentTransform.Inverse();
AsyncTransformComponentMatrix asyncCompensation =
ViewAs<AsyncTransformComponentMatrix>(
contentTransform
* asyncUntransform
* contentUntransform);
compensation = compensation * asyncCompensation;
- // We also need to make a corresponding change on the clip rect of all the
- // layers on the ancestor chain from the scrollbar layer up to but not
- // including the layer with the async transform. Otherwise the scrollbar
- // shifts but gets clipped and so appears to flicker.
- for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
- TransformClipRect(ancestor, asyncCompensation);
+ // Pass the async compensation out to the caller so that it can use it
+ // to transform clip transforms as needed.
+ if (aOutClipTransform) {
+ *aOutClipTransform = asyncCompensation;
}
}
transform = transform * compensation;
- SetShadowTransform(aScrollbar, transform);
+ return transform;
}
static LayerMetricsWrapper
FindScrolledLayerForScrollbar(Layer* aScrollbar, bool* aOutIsAncestor)
{
// First check if the scrolled layer is an ancestor of the scrollbar layer.
LayerMetricsWrapper root(aScrollbar->Manager()->GetRoot());
LayerMetricsWrapper prevAncestor(aScrollbar);
--- a/gfx/layers/composite/AsyncCompositionManager.h
+++ b/gfx/layers/composite/AsyncCompositionManager.h
@@ -126,16 +126,48 @@ public:
Maybe<ParentLayerIntRect> mScrolledClip;
Maybe<ParentLayerIntRect> Intersect() const {
return IntersectMaybeRects(mFixedClip, mScrolledClip);
}
};
typedef std::map<Layer*, ClipParts> ClipPartsCache;
+
+ /**
+ * Compute the updated shadow transform for a scroll thumb layer that
+ * reflects async scrolling of the associated scroll frame.
+ *
+ * @param aCurrentTransform The current shadow transform on the scroll thumb
+ * layer, as returned by Layer::GetLocalTransform() or similar.
+ * @param aScrollableContentTransform The current content transform on the
+ * scrollable content, as returned by Layer::GetTransform().
+ * @param aApzc The APZC that scrolls the scroll frame.
+ * @param aMetrics The metrics associated with the scroll frame, reflecting
+ * the last paint of the associated content. Note: this metrics should
+ * NOT reflect async scrolling, i.e. they should be the layer tree's
+ * copy of the metrics, or APZC's last-content-paint metrics.
+ * @param aThumbData The scroll thumb data for the the scroll thumb layer.
+ * @param aScrollbarIsDescendant True iff. the scroll thumb layer is a
+ * descendant of the layer bearing the scroll frame's metrics.
+ * @param aOutClipTransform If not null, and |aScrollbarIsDescendant| is true,
+ * this will be populated with a transform that should be applied to the
+ * clip rects of all layers between the scroll thumb layer and the ancestor
+ * layer for the scrollable content.
+ * @return The new shadow transform for the scroll thumb layer, including
+ * any pre- or post-scales.
+ */
+ static LayerToParentLayerMatrix4x4 ComputeTransformForScrollThumb(
+ const LayerToParentLayerMatrix4x4& aCurrentTransform,
+ const gfx::Matrix4x4& aScrollableContentTransform,
+ AsyncPanZoomController* aApzc,
+ const FrameMetrics& aMetrics,
+ const ScrollThumbData& aThumbData,
+ bool aScrollbarIsDescendant,
+ AsyncTransformComponentMatrix* aOutClipTransform);
private:
// Return true if an AsyncPanZoomController content transform was
// applied for |aLayer|. |*aOutFoundRoot| is set to true on Android only, if
// one of the metrics on one of the layers was determined to be the "root"
// and its state was synced to the Java front-end. |aOutFoundRoot| must be
// non-null.
bool ApplyAsyncContentTransformToTree(Layer* aLayer,
bool* aOutFoundRoot);