| 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));
|
| }
|
| }
|
|
|