Bug 784908: Part 3: Distinguish resolution from a new zoom field on FrameMetrics r=roc

This commit is contained in:
Doug Sherk 2012-09-28 22:16:38 -04:00
parent b3a78c0ca6
commit 98c60b046f
6 changed files with 82 additions and 47 deletions

View File

@ -847,7 +847,7 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
// We don't treat the x and y scales any differently for this
// semi-platform-specific code.
data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mResolution.width);
data += nsPrintfCString(", \"zoom\" : %f", aFrameMetrics.mZoom.width);
data += nsPrintfCString(", \"displayPort\" : ");
data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x);
data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y);

View File

@ -184,10 +184,33 @@ public:
// This is valid on any layer unless it has no content.
gfx::Rect mScrollableRect;
// This represents the resolution at which the associated layer
// will been rendered.
// ---------------------------------------------------------------------------
// The following metrics are dimensionless.
//
// The resolution, along both axes, that the current frame has been painted
// at.
//
// Every time this frame is composited and the compositor samples its
// transform, this metric is used to create a transform which is
// post-multiplied into the parent's transform. Since this only happens when
// we walk the layer tree, the resulting transform isn't stored here. Thus the
// resolution of parent layers is opaque to this metric.
gfxSize mResolution;
// The amount we are currently zooming the frame. This is distinct from
// |mResolution| as we can paint a frame at a different resolution than we
// zoom it at. This is useful in situations where we want to zoom a frame
// without forcing a repaint. At steady state, this and |mResolution| will be
// equal.
//
// Every time this frame is composited and the compositor samples its
// transform, this metric is used to create a transform which is
// post-multiplied into the parent's transform. Since this only happens when
// we walk the layer tree, the resulting transform isn't stored here. Thus the
// zoom of parent layers is opaque to this metric.
gfxSize mZoom;
// The conversion factor between CSS pixels and device pixels for this frame.
// This can vary based on a variety of things, such as reflowing-zoom. The
// conversion factor for device pixels to layers pixels is just the

View File

@ -137,7 +137,7 @@ AsyncPanZoomController::ReceiveInputEvent(const nsInputEvent& aEvent,
gfx::Point currentScrollOffset, lastScrollOffset;
{
MonitorAutoLock monitor(mMonitor);
currentZoom = mFrameMetrics.mResolution.width;
currentZoom = mFrameMetrics.mZoom.width;
currentScrollOffset = gfx::Point(mFrameMetrics.mScrollOffset.x,
mFrameMetrics.mScrollOffset.y);
lastScrollOffset = gfx::Point(mLastContentPaintMetrics.mScrollOffset.x,
@ -284,8 +284,13 @@ nsEventStatus AsyncPanZoomController::OnTouchStart(const MultiTouchInput& aEvent
case ANIMATING_ZOOM:
// We just interrupted a double-tap animation, so force a redraw in case
// this touchstart is just a tap that doesn't end up triggering a redraw.
RequestContentRepaint();
ScheduleComposite();
{
MonitorAutoLock monitor(mMonitor);
// Bring the resolution back in sync with the zoom.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
RequestContentRepaint();
ScheduleComposite();
}
// Fall through.
case FLING:
CancelAnimation();
@ -422,7 +427,7 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
{
MonitorAutoLock monitor(mMonitor);
float scale = mFrameMetrics.mResolution.width;
float scale = mFrameMetrics.mZoom.width;
nsIntPoint focusPoint = aEvent.mFocusPoint;
float xFocusChange = (mLastZoomFocus.x - focusPoint.x) / scale, yFocusChange = (mLastZoomFocus.y - focusPoint.y) / scale;
@ -529,7 +534,7 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mResolution.width);
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleSingleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
return nsEventStatus_eConsumeNoDefault;
}
@ -547,7 +552,7 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mResolution.width);
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
return nsEventStatus_eConsumeNoDefault;
}
@ -614,7 +619,7 @@ void AsyncPanZoomController::TrackTouch(const MultiTouchInput& aEvent) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mResolution.width;
float inverseScale = 1 / mFrameMetrics.mZoom.width;
int32_t xDisplacement = mX.GetDisplacementForDuration(inverseScale, timeDelta);
int32_t yDisplacement = mY.GetDisplacementForDuration(inverseScale, timeDelta);
@ -649,7 +654,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
// We want to inversely scale it because when you're zoomed further in, a
// larger swipe should move you a shorter distance.
float inverseScale = 1 / mFrameMetrics.mResolution.width;
float inverseScale = 1 / mFrameMetrics.mZoom.width;
ScrollBy(gfx::Point(
mX.GetDisplacementForDuration(inverseScale, aDelta),
@ -679,7 +684,7 @@ void AsyncPanZoomController::ScrollBy(const gfx::Point& aOffset) {
void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
FrameMetrics metrics = mFrameMetrics;
gfx::Rect pageSize = aCSSPageRect;
float scale = mFrameMetrics.mResolution.width;
float scale = mFrameMetrics.mZoom.width;
// The page rect is the css page rect scaled by the current zoom.
pageSize.ScaleInverseRoundOut(scale);
@ -693,26 +698,17 @@ void AsyncPanZoomController::SetPageRect(const gfx::Rect& aCSSPageRect) {
}
void AsyncPanZoomController::ScaleWithFocus(float aScale, const nsIntPoint& aFocus) {
FrameMetrics metrics(mFrameMetrics);
float scaleFactor = aScale / mFrameMetrics.mZoom.width,
oldScale = mFrameMetrics.mZoom.width;
// Don't set the scale to the inputted value, but rather multiply it in.
float scaleFactor = aScale / metrics.mResolution.width,
oldScale = metrics.mResolution.width;
metrics.mResolution.width = metrics.mResolution.height = aScale;
SetZoomAndResolution(aScale);
// Force a recalculation of the page rect based on the new zoom and the
// current CSS page rect (which is unchanged since it's not affected by zoom).
SetPageRect(mFrameMetrics.mScrollableRect);
gfx::Point scrollOffset = metrics.mScrollOffset;
scrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale;
scrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale;
metrics.mScrollOffset = scrollOffset;
mFrameMetrics = metrics;
mFrameMetrics.mScrollOffset.x += float(aFocus.x) * (scaleFactor - 1.0f) / oldScale;
mFrameMetrics.mScrollOffset.y += float(aFocus.y) * (scaleFactor - 1.0f) / oldScale;
}
bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBounds,
@ -733,7 +729,7 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBound
}
const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() {
float scale = mFrameMetrics.mResolution.width;
float scale = mFrameMetrics.mZoom.width;
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
compositionBounds.ScaleInverseRoundIn(scale);
@ -852,9 +848,9 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
}
double sampledPosition = gComputedTimingFunction->GetValue(animPosition);
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
mEndZoomToMetrics.mResolution.width * sampledPosition +
mStartZoomToMetrics.mResolution.width * (1 - sampledPosition);
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height =
mEndZoomToMetrics.mZoom.width * sampledPosition +
mStartZoomToMetrics.mZoom.width * (1 - sampledPosition);
mFrameMetrics.mScrollOffset = gfx::Point(
mEndZoomToMetrics.mScrollOffset.x * sampledPosition +
@ -866,6 +862,8 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
requestAnimationFrame = true;
if (aSampleTime - mAnimationStartTime >= ZOOM_TO_DURATION) {
// Bring the resolution in sync with the zoom.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
mState = NOTHING;
RequestContentRepaint();
}
@ -880,8 +878,8 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
// transformed due to touches like panning or pinching. Eventually, the root
// layer transform will become this during runtime, but we must wait for Gecko
// to repaint.
localScaleX = mFrameMetrics.mResolution.width;
localScaleY = mFrameMetrics.mResolution.height;
localScaleX = mFrameMetrics.mZoom.width;
localScaleY = mFrameMetrics.mZoom.height;
if (frame.IsScrollable()) {
metricsScrollOffset = frame.GetScrollOffsetInLayerPixels();
@ -936,10 +934,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
case FLING:
case TOUCHING:
case WAITING_LISTENERS:
// FIXME/bug 784908: Scroll offset is stored in layer pixels in the rest
// of the layers code, but we want it in CSS pixels.
mFrameMetrics.mScrollOffset =
aViewportFrame.mScrollOffset / aViewportFrame.mResolution.width;
mFrameMetrics.mScrollOffset = aViewportFrame.mScrollOffset;
break;
// Don't clobber if we're in other states.
default:
@ -958,8 +953,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
mFrameMetrics = aViewportFrame;
mFrameMetrics.mCompositionBounds = compositionBounds;
mFrameMetrics.mResolution.width = 1 / mFrameMetrics.mResolution.width;
mFrameMetrics.mResolution.height = 1 / mFrameMetrics.mResolution.height;
// On first paint, we want to bring zoom back in sync with resolution.
mFrameMetrics.mZoom = mFrameMetrics.mResolution;
SetPageRect(mFrameMetrics.mScrollableRect);
// Bug 776413/fixme: Request a repaint as soon as a page is loaded so that
@ -1010,7 +1006,7 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
if (zoomToRect.IsEmpty()) {
// composition bounds in CSS coordinates
nsIntRect cssCompositionBounds = compositionBounds;
cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mResolution.width);
cssCompositionBounds.ScaleInverseRoundIn(mFrameMetrics.mZoom.width);
cssCompositionBounds.MoveBy(scrollOffset.x, scrollOffset.y);
float y = mFrameMetrics.mScrollOffset.y;
@ -1043,21 +1039,21 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
zoomToRect = zoomToRect.Intersect(cssPageRect);
}
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
clamped(mEndZoomToMetrics.mResolution.width, MIN_ZOOM, MAX_ZOOM);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
clamped(mEndZoomToMetrics.mZoom.width, MIN_ZOOM, MAX_ZOOM);
// Recalculate the zoom to rect using the new dimensions.
zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mResolution.width;
zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mResolution.height;
zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width;
zoomToRect.height = compositionBounds.height / mEndZoomToMetrics.mZoom.height;
// Clamp the zoom to rect to the CSS rect to make sure it fits.
zoomToRect = zoomToRect.Intersect(cssPageRect);
// Do one final recalculation to get the resolution.
mEndZoomToMetrics.mResolution.width = mEndZoomToMetrics.mResolution.height =
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
NS_MAX(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mStartZoomToMetrics = mFrameMetrics;
@ -1115,5 +1111,11 @@ void AsyncPanZoomController::TimeoutTouchListeners() {
ContentReceivedTouch(false);
}
void AsyncPanZoomController::SetZoomAndResolution(float aScale) {
mMonitor.AssertCurrentThreadOwns();
mFrameMetrics.mResolution.width = mFrameMetrics.mResolution.height =
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale;
}
}
}

View File

@ -392,6 +392,14 @@ protected:
*/
void TimeoutTouchListeners();
/**
* Utility function that sets the zoom and resolution simultaneously. This is
* useful when we want to repaint at the current zoom level.
*
* *** The monitor must be held while calling this.
*/
void SetZoomAndResolution(float aScale);
private:
enum PanZoomState {
NOTHING, /* no touch-start events received */

View File

@ -251,7 +251,7 @@ float Axis::GetCompositionLength() {
gfx::Rect(compositionBounds.x, compositionBounds.y,
compositionBounds.width, compositionBounds.height);
scaledCompositionBounds.ScaleInverseRoundIn(
mAsyncPanZoomController->GetFrameMetrics().mResolution.width);
mAsyncPanZoomController->GetFrameMetrics().mZoom.width);
return GetRectLength(scaledCompositionBounds);
}
@ -270,7 +270,7 @@ bool Axis::ScaleWillOverscrollBothSides(float aScale) {
gfx::Rect cssContentRect = metrics.mScrollableRect;
float currentScale = metrics.mResolution.width;
float currentScale = metrics.mZoom.width;
nsIntRect compositionBounds = metrics.mCompositionBounds;
gfx::Rect scaledCompositionBounds =
gfx::Rect(compositionBounds.x, compositionBounds.y,

View File

@ -968,6 +968,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
WriteParam(aMsg, aParam.mCompositionBounds);
WriteParam(aMsg, aParam.mScrollId);
WriteParam(aMsg, aParam.mResolution);
WriteParam(aMsg, aParam.mZoom);
WriteParam(aMsg, aParam.mDevPixelsPerCSSPixel);
WriteParam(aMsg, aParam.mMayHaveTouchListeners);
}
@ -982,6 +983,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
ReadParam(aMsg, aIter, &aResult->mCompositionBounds) &&
ReadParam(aMsg, aIter, &aResult->mScrollId) &&
ReadParam(aMsg, aIter, &aResult->mResolution) &&
ReadParam(aMsg, aIter, &aResult->mZoom) &&
ReadParam(aMsg, aIter, &aResult->mDevPixelsPerCSSPixel) &&
ReadParam(aMsg, aIter, &aResult->mMayHaveTouchListeners));
}