Index: remoting/android/java/src/org/chromium/chromoting/DesktopView.java |
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java |
index c7222577c78ca576a9656fff46ac66c10cd7774d..b7666b303364faa325f5c534c1f1aab3c4edac87 100644 |
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java |
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java |
@@ -57,6 +57,12 @@ public class DesktopView extends SurfaceView implements Runnable, SurfaceHolder. |
/** Specifies the dimension by which the zoom level is being lower-bounded. */ |
private Constraint mConstraint; |
+ /** Whether the right edge of the image was visible on-screen during the last render. */ |
+ private boolean mRightUsedToBeOut; |
+ |
+ /** Whether the bottom edge of the image was visible on-screen during the last render. */ |
+ private boolean mBottomUsedToBeOut; |
+ |
/** Whether the device has just been rotated, necessitating a canvas redraw. */ |
private boolean mJustRotated; |
@@ -71,6 +77,11 @@ public class DesktopView extends SurfaceView implements Runnable, SurfaceHolder. |
mScreenWidth = 0; |
mScreenHeight = 0; |
mConstraint = Constraint.UNDEFINED; |
+ |
+ mRightUsedToBeOut = false; |
+ mBottomUsedToBeOut = false; |
+ |
+ mJustRotated = false; |
} |
/** |
@@ -80,46 +91,88 @@ public class DesktopView extends SurfaceView implements Runnable, SurfaceHolder. |
*/ |
@Override |
public void run() { |
- if (Looper.myLooper()==Looper.getMainLooper()) { |
+ if (Looper.myLooper() == Looper.getMainLooper()) { |
Log.w("deskview", "Canvas being redrawn on UI thread"); |
} |
- Canvas canvas = getHolder().lockCanvas(); |
Bitmap image = JniInterface.retrieveVideoFrame(); |
+ Canvas canvas = getHolder().lockCanvas(); |
synchronized (mTransform) { |
canvas.setMatrix(mTransform); |
+ // Internal parameters of the transformation matrix. |
float[] values = new float[9]; |
mTransform.getValues(values); |
+ |
+ // Screen coordinates of two defining points of the image. |
float[] topleft = {0, 0}; |
mTransform.mapPoints(topleft); |
float[] bottomright = {image.getWidth(), image.getHeight()}; |
mTransform.mapPoints(bottomright); |
- if (mConstraint==Constraint.UNDEFINED) { |
+ // Whether to rescale and recenter the view. |
+ boolean recenter = false; |
+ |
+ if (mConstraint == Constraint.UNDEFINED) { |
mConstraint = image.getWidth()/image.getHeight() > mScreenWidth/mScreenHeight ? |
- Constraint.HEIGHT : Constraint.WIDTH; |
+ Constraint.WIDTH : Constraint.HEIGHT; |
+ recenter = true; // We always rescale and recenter after a rotation. |
} |
- if (mConstraint==Constraint.WIDTH && bottomright[0] - topleft[0] < mScreenWidth) { |
- mTransform.setPolyToPoly(new float[] {0, 0, image.getWidth(), 0}, 0, |
- new float[] {0, 0, mScreenWidth, 0}, 0, 2); |
- } else if (mConstraint==Constraint.HEIGHT && |
- bottomright[1] - topleft[1] < mScreenHeight) { |
- mTransform.setPolyToPoly(new float[] {0, 0, 0, image.getHeight()}, 0, |
- new float[] {0, 0, 0, mScreenHeight}, 0, 2); |
+ if (mConstraint == Constraint.WIDTH && |
+ ((int)(bottomright[0] - topleft[0] + 0.5) < mScreenWidth || recenter)) { |
+ // The vertical edges of the image are flush against the device's screen edges |
+ // when the entire host screen is visible, and the user has zoomed out too far. |
+ float imageMiddle = (float)image.getHeight() / 2; |
+ float screenMiddle = (float)mScreenHeight / 2; |
+ mTransform.setPolyToPoly( |
+ new float[] {0, imageMiddle, image.getWidth(), imageMiddle}, 0, |
+ new float[] {0, screenMiddle, mScreenWidth, screenMiddle}, 0, 2); |
+ } else if (mConstraint == Constraint.HEIGHT && |
+ ((int)(bottomright[1] - topleft[1] + 0.5) < mScreenHeight || recenter)) { |
+ // The horizontal image edges are flush against the device's screen edges when |
+ // the entire host screen is visible, and the user has zoomed out too far. |
+ float imageCenter = (float)image.getWidth() / 2; |
+ float screenCenter = (float)mScreenWidth / 2; |
+ mTransform.setPolyToPoly( |
+ new float[] {imageCenter, 0, imageCenter, image.getHeight()}, 0, |
+ new float[] {screenCenter, 0, screenCenter, mScreenHeight}, 0, 2); |
} else { |
- if (values[Matrix.MTRANS_X] > 0) { |
- values[Matrix.MTRANS_X] = 0; |
+ // It's fine for both members of a pair of image edges to be within the screen |
+ // edges (or "out of bounds"); that simply means that the image is zoomed out as |
+ // far as permissible. And both members of a pair can obviously be outside the |
+ // screen's edges, which indicates that the image is zoomed in to far to see the |
+ // whole host screen. However, if only one of a pair of edges has entered the |
+ // screen, the user is attempting to scroll into a blank area of the canvas. |
+ |
+ // A value of true means the corresponding edge has entered the screen's borders. |
+ boolean leftEdgeOutOfBounds = values[Matrix.MTRANS_X] > 0; |
+ boolean topEdgeOutOfBounds = values[Matrix.MTRANS_Y] > 0; |
+ boolean rightEdgeOutOfBounds = bottomright[0] < mScreenWidth; |
+ boolean bottomEdgeOutOfBounds = bottomright[1] < mScreenHeight; |
+ |
+ if (leftEdgeOutOfBounds != rightEdgeOutOfBounds) { |
+ if (leftEdgeOutOfBounds != mRightUsedToBeOut) { |
+ values[Matrix.MTRANS_X] = 0; |
+ } |
+ else { |
+ values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0]; |
+ } |
} |
- if (values[Matrix.MTRANS_Y] > 0) { |
- values[Matrix.MTRANS_Y] = 0; |
+ else { // The view would oscillate if this were updated while scrolling off-screen. |
+ mRightUsedToBeOut = rightEdgeOutOfBounds; |
} |
- if (bottomright[0] < mScreenWidth) { |
- values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0]; |
+ |
+ if (topEdgeOutOfBounds != bottomEdgeOutOfBounds) { |
+ if (topEdgeOutOfBounds != mBottomUsedToBeOut) { |
+ values[Matrix.MTRANS_Y] = 0; |
+ } |
+ else { |
+ values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1]; |
+ } |
} |
- if (bottomright[1] < mScreenHeight) { |
- values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1]; |
+ else { // The view would oscillate if this were updated while scrolling off-screen. |
+ mBottomUsedToBeOut = bottomEdgeOutOfBounds; |
} |
mTransform.setValues(values); |