Index: remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
index e6ae76baff7915e64059f84a2fa288be564d169e..778c395267e875f75548e8ff8aa6acc33aa484e0 100644 |
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
@@ -14,32 +14,49 @@ import android.graphics.RectF; |
*/ |
public class DesktopCanvas { |
/** |
- * Maximum allowed zoom level - see {@link #repositionImageWithZoom()}. |
+ * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. |
*/ |
private static final float MAX_ZOOM_FACTOR = 100.0f; |
+ /** |
+ * Used to smoothly reduce the amount of padding while the user is zooming. |
+ */ |
+ private static final float PADDING_REDUCTION_FACTOR = 0.85f; |
+ |
private final RenderStub mRenderStub; |
private final RenderData mRenderData; |
/** |
- * Represents the actual center of the viewport. This value needs to be a pair of floats so the |
- * desktop image can be positioned with sub-pixel accuracy for smoother panning animations at |
- * high zoom levels. |
+ * Represents the actual center of the viewport in image space. This value needs to be a pair |
+ * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning |
+ * animations at high zoom levels. |
*/ |
+ // TODO(joedow): See if we can collapse Viewport and Cursor position members. They were needed |
+ // in the past due to how we calculated the center positions but may not be needed now. |
private PointF mViewportPosition = new PointF(); |
/** |
- * Represents the desired center of the viewport. This value may not represent the actual |
- * center of the viewport as adjustments are made to ensure as much of the desktop is visible as |
- * possible. This value needs to be a pair of floats so the desktop image can be positioned |
- * with sub-pixel accuracy for smoother panning animations at high zoom levels. |
+ * Represents the desired center of the viewport in image space. This value may not represent |
+ * the actual center of the viewport as adjustments are made to ensure as much of the desktop is |
+ * visible as possible. This value needs to be a pair of floats so the desktop image can be |
+ * positioned with sub-pixel accuracy for smoother panning animations at high zoom levels. |
+ * Note: The internal cursor position may be placed outside of the image boundary, however the |
+ * cursor position we inject on the host side is restricted to the actual image dimensions. |
*/ |
private PointF mCursorPosition = new PointF(); |
/** |
- * Represents the amount of space, in pixels, used by System UI. |
+ * Represents the amount of space, in pixels, used by System UI on each edge of the screen. |
+ */ |
+ // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets. |
+ private Rect mSystemUiScreenSize = new Rect(); |
+ |
+ /** |
+ * Represents the amount of padding, in screen pixels, added to each edge of the desktop image. |
+ * This extra space allows the user to reveal portions of the desktop image which are obscured |
+ * by System UI. |
*/ |
- private Rect mSystemUiOffsetPixels = new Rect(); |
+ private RectF mVisibleImagePadding = new RectF(); |
public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
mRenderStub = renderStub; |
@@ -104,12 +121,12 @@ public class DesktopCanvas { |
* @param bottom The space used by System UI on the bottom edge of the screen. |
*/ |
public void setSystemUiOffsetValues(int left, int top, int right, int bottom) { |
- mSystemUiOffsetPixels.set(left, top, right, bottom); |
+ mSystemUiScreenSize.set(left, top, right, bottom); |
} |
/** Called to indicate that no System UI is visible. */ |
public void clearSystemUiOffsets() { |
- mSystemUiOffsetPixels.setEmpty(); |
+ mSystemUiScreenSize.setEmpty(); |
} |
/** Resizes the image by zooming it such that the image is displayed without borders. */ |
@@ -169,6 +186,16 @@ public class DesktopCanvas { |
mRenderData.transform.setScale(scale, scale); |
} |
+ // Trim the image padding if the user is zooming out. This prevents cases where the image |
+ // pops to the center when it reaches its minimum size. Note that we do not need to do |
+ // anything when the user is zooming in as the canvas will expand and absorb the padding. |
+ if (scaleFactor < 1.0f) { |
+ mVisibleImagePadding.set(mVisibleImagePadding.left * PADDING_REDUCTION_FACTOR, |
+ mVisibleImagePadding.top * PADDING_REDUCTION_FACTOR, |
+ mVisibleImagePadding.right * PADDING_REDUCTION_FACTOR, |
+ mVisibleImagePadding.bottom * PADDING_REDUCTION_FACTOR); |
+ } |
+ |
if (centerOnCursor) { |
setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
} else { |
@@ -198,6 +225,8 @@ public class DesktopCanvas { |
// Translate the image so the viewport center is displayed in the middle of the screen. |
mRenderData.transform.postTranslate(viewportTransX, viewportTransY); |
+ updateVisibleImagePadding(); |
+ |
mRenderStub.setTransformation(mRenderData.transform); |
} |
@@ -221,35 +250,97 @@ public class DesktopCanvas { |
} |
} |
- /** Returns a region which defines the set of valid cursor values in image space. */ |
+ /** Returns a region which defines the set of valid cursor positions in image space. */ |
private RectF getImageBounds() { |
- return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
+ // The set of valid cursor positions includes any point on the image as well as the padding. |
+ // Padding is additional space added to the image which is the larger value of: |
+ // - Potential overlap of the System UI and image content |
+ // - Actual amount of padding already being used |
+ // |
+ // By expanding the area, we allow the user to move the cursor 'under' the System UI which |
+ // pulls the content out from under it and allows it to be visible. Once the System UI has |
+ // been dismissed or changes size, we use the actual padding value instead which prevents |
+ // the desktop image from 'snapping' back to pre-System UI state. |
+ RectF systemUiOverlap = getSystemUiOverlap(); |
+ float[] padding = {Math.max(mVisibleImagePadding.left, systemUiOverlap.left), |
+ Math.max(mVisibleImagePadding.top, systemUiOverlap.top), |
+ Math.max(mVisibleImagePadding.right, systemUiOverlap.right), |
+ Math.max(mVisibleImagePadding.bottom, systemUiOverlap.bottom)}; |
+ Matrix screenToImage = new Matrix(); |
+ mRenderData.transform.invert(screenToImage); |
+ screenToImage.mapVectors(padding); |
+ |
+ return new RectF(-padding[0], -padding[1], mRenderData.imageWidth + padding[2], |
+ mRenderData.imageHeight + padding[3]); |
} |
/** Returns a region which defines the set of valid viewport center values in image space. */ |
private RectF getViewportBounds() { |
- float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRenderData.screenHeight}; |
+ // The region of allowable viewport values is the imageBound rect, inset by the size of the |
+ // viewport itself. This prevents over and under panning of the viewport while still |
+ // allowing the user to see and interact with all pixels one the desktop image. |
Matrix screenToImage = new Matrix(); |
mRenderData.transform.invert(screenToImage); |
- screenToImage.mapVectors(screenVectors); |
- float xAdjust = 0.0f; |
- if (mRenderData.imageWidth < screenVectors[0]) { |
- // Image is narrower than the screen, so adjust the bounds to center it. |
- xAdjust = (screenVectors[0] - mRenderData.imageWidth) / 2.0f; |
- } |
+ float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRenderData.screenHeight}; |
+ screenToImage.mapVectors(screenVectors); |
- float yAdjust = 0.0f; |
- if (mRenderData.imageHeight < screenVectors[1]) { |
- // Image is shorter than the screen, so adjust the bounds to center it. |
- yAdjust = (screenVectors[1] - mRenderData.imageHeight) / 2.0f; |
- } |
+ PointF letterboxPadding = getLetterboxPadding(); |
+ float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding.y}; |
+ screenToImage.mapVectors(letterboxPaddingVectors); |
// screenCenter values are 1/2 of a particular screen dimension mapped to image space. |
- float screenCenterX = screenVectors[0] / 2.0f; |
- float screenCenterY = screenVectors[1] / 2.0f; |
- return new RectF(screenCenterX - xAdjust, screenCenterY - yAdjust, |
- mRenderData.imageWidth - screenCenterX + xAdjust, |
- mRenderData.imageHeight - screenCenterY + yAdjust); |
+ float screenCenterX = (screenVectors[0] / 2.0f) - letterboxPaddingVectors[0]; |
+ float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVectors[1]; |
+ RectF imageBounds = getImageBounds(); |
+ imageBounds.inset(screenCenterX, screenCenterY); |
+ return imageBounds; |
+ } |
+ |
+ /** |
+ * Provides the amount of padding needed to center the image content on the screen. |
+ */ |
+ private PointF getLetterboxPadding() { |
+ float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight}; |
+ mRenderData.transform.mapVectors(imageVectors); |
+ |
+ // We want to letterbox when the image is smaller than the screen in a specific dimension. |
+ // Since we center the image, split the difference so it is equally distributed. |
+ float widthAdjust = |
+ Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2.0f, 0.0f); |
+ float heightAdjust = |
+ Math.max(((float) mRenderData.screenHeight - imageVectors[1]) / 2.0f, 0.0f); |
+ |
+ return new PointF(widthAdjust, heightAdjust); |
+ } |
+ |
+ /** |
+ * Returns the amount of System UI along each edge of the screen which could overlap the remote |
+ * desktop image below it. This is the maximum amount that could overlap, not the actual value. |
+ */ |
+ private RectF getSystemUiOverlap() { |
+ // letterBox padding represents the space added to the image to center it on the screen. |
+ // Since it does not contain any interactable UI, we ignore it when calculating the overlap |
+ // between the System UI and the remote desktop image. |
+ // Note: Ignore negative padding (clamp to 0) since that means no overlap exists. |
+ PointF letterboxPadding = getLetterboxPadding(); |
+ return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), |
+ Math.max(mSystemUiScreenSize.top - letterboxPadding.y, 0.0f), |
+ Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), |
+ Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y, 0.0f)); |
+ } |
+ |
+ /** |
+ * Calculates the amount of padding visible on each edge of the desktop image. |
+ */ |
+ private void updateVisibleImagePadding() { |
+ PointF letterboxPadding = getLetterboxPadding(); |
+ float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.imageHeight}; |
+ mRenderData.transform.mapPoints(imagePoints); |
+ |
+ mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0.0f), |
+ Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), |
+ Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPadding.x, 0.0f), |
+ Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPadding.y, 0.0f)); |
} |
} |