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 35f9ee6709c1beefaa4272a8919d23fdb0364f99..e6ae76baff7915e64059f84a2fa288be564d169e 100644 |
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java |
@@ -22,12 +22,19 @@ public class DesktopCanvas { |
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. |
+ */ |
+ 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. |
*/ |
- private PointF mViewportPosition = new PointF(); |
+ private PointF mCursorPosition = new PointF(); |
/** |
* Represents the amount of space, in pixels, used by System UI. |
@@ -40,67 +47,52 @@ public class DesktopCanvas { |
} |
/** |
- * Shifts the viewport by the passed in deltas (in image coordinates). |
+ * Sets the desired center position of the viewport (a.k.a. the cursor position) and ensures |
+ * the viewport is updated to include the cursor within it. |
* |
- * @param useScreenCenter Determines whether to use the desired viewport position or the actual |
- * center of the screen for positioning. |
- * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis. |
- * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis. |
- * @return A point representing the new center position of the viewport. |
+ * @param newX The new x coordinate value for the desired center position. |
+ * @param newY The new y coordinate value for the desired center position. |
*/ |
- public PointF moveViewportCenter(boolean useScreenCenter, float deltaX, float deltaY) { |
- PointF viewportCenter; |
- if (useScreenCenter) { |
- viewportCenter = getTrueViewportCenter(); |
- } else { |
- viewportCenter = new PointF(mViewportPosition.x, mViewportPosition.y); |
- } |
- viewportCenter.offset(deltaX, deltaY); |
+ public void setCursorPosition(float newX, float newY) { |
+ // First set the cursor position since its potential values are a superset of the viewport. |
+ mCursorPosition.set(newX, newY); |
+ constrainPointToBounds(mCursorPosition, getImageBounds()); |
- RectF bounds = new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
+ // Now set the viewport position based on the cursor. |
+ mViewportPosition.set(mCursorPosition); |
+ constrainPointToBounds(mViewportPosition, getViewportBounds()); |
- if (viewportCenter.x < bounds.left) { |
- viewportCenter.x = bounds.left; |
- } else if (viewportCenter.x > bounds.right) { |
- viewportCenter.x = bounds.right; |
- } |
- |
- if (viewportCenter.y < bounds.top) { |
- viewportCenter.y = bounds.top; |
- } else if (viewportCenter.y > bounds.bottom) { |
- viewportCenter.y = bounds.bottom; |
- } |
- |
- mViewportPosition.set(viewportCenter); |
- |
- return viewportCenter; |
+ repositionImage(); |
} |
/** |
- * Sets the desired center position of the viewport. |
+ * Shifts the viewport by the passed in values (in image coordinates). |
* |
- * @param newX The new x coordinate value for the desired center position. |
- * @param newY The new y coordinate value for the desired center position. |
+ * @param deltaX The distance (in image coordinates) to move the viewport along the x-axis. |
+ * @param deltaY The distance (in image coordinates) to move the viewport along the y-axis. |
*/ |
- public void setViewportPosition(float newX, float newY) { |
- mViewportPosition.set(newX, newY); |
+ public void moveViewportCenter(float deltaX, float deltaY) { |
+ // Offset and adjust the viewport center position to fit the screen. |
+ mViewportPosition.offset(deltaX, deltaY); |
+ constrainPointToBounds(mViewportPosition, getViewportBounds()); |
+ |
+ // We don't need to constrain the cursor position as the viewport position is always within |
+ // the bounds of the potential cursor positions. |
+ mCursorPosition.set(mViewportPosition); |
+ |
+ repositionImage(); |
} |
/** |
- * Returns the true center position of the viewport (in image coordinates). |
+ * Shifts the cursor by the passed in values (in image coordinates) and adjusts the viewport. |
* |
- * @return A point representing the true center position of the viewport. |
+ * @param deltaX The distance (in image coordinates) to move the cursor along the x-axis. |
+ * @param deltaY The distance (in image coordinates) to move the cursor along the y-axis. |
+ * @return A point representing the new cursor position. |
*/ |
- private PointF getTrueViewportCenter() { |
- // Find the center point of the viewport on the screen. |
- float[] viewportPosition = { |
- ((float) mRenderData.screenWidth / 2), ((float) mRenderData.screenHeight / 2)}; |
- |
- // Convert the screen position to an image position. |
- Matrix screenToImage = new Matrix(); |
- mRenderData.transform.invert(screenToImage); |
- screenToImage.mapPoints(viewportPosition); |
- return new PointF(viewportPosition[0], viewportPosition[1]); |
+ public PointF moveCursorPosition(float deltaX, float deltaY) { |
+ setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY); |
+ return new PointF(mCursorPosition.x, mCursorPosition.y); |
} |
/** |
@@ -120,7 +112,7 @@ public class DesktopCanvas { |
mSystemUiOffsetPixels.setEmpty(); |
} |
- /** Repositions the image by zooming it such that the image is displayed without borders. */ |
+ /** Resizes the image by zooming it such that the image is displayed without borders. */ |
public void resizeImageToFitScreen() { |
// Protect against being called before the image has been initialized. |
if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { |
@@ -136,70 +128,6 @@ public class DesktopCanvas { |
if (screenToImageScale > 1.0f) { |
mRenderData.transform.setScale(screenToImageScale, screenToImageScale); |
} |
- |
- repositionImage(false); |
- } |
- |
- /** |
- * Repositions the image by translating it (without affecting the zoom level). |
- * |
- * @param centerViewport Determines whether the viewport will be translated to the desired |
- * center position before being adjusted to fit the screen boundaries. |
- */ |
- public void repositionImage(boolean centerViewport) { |
- // The goal of the code below is to position the viewport as close to the desired center |
- // position as possible whilst keeping as much of the desktop in view as possible. |
- // To achieve these goals, we first position the desktop image at the desired center |
- // point and then re-position it to maximize the viewable area. |
- if (centerViewport) { |
- // Map the current viewport position to screen coordinates. |
- float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; |
- mRenderData.transform.mapPoints(viewportPosition); |
- |
- float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportPosition[0]; |
- float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewportPosition[1]; |
- |
- // Translate so the viewport is displayed in the middle of the screen. |
- mRenderData.transform.postTranslate(viewportTransX, viewportTransY); |
- } |
- |
- // Get the coordinates of the desktop rectangle (top-left/bottom-right corners) in |
- // screen coordinates. Order is: left, top, right, bottom. |
- RectF rectScreen = new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
- mRenderData.transform.mapRect(rectScreen); |
- |
- float leftDelta = rectScreen.left; |
- float rightDelta = |
- rectScreen.right - mRenderData.screenWidth + mSystemUiOffsetPixels.right; |
- float topDelta = rectScreen.top; |
- float bottomDelta = |
- rectScreen.bottom - mRenderData.screenHeight + mSystemUiOffsetPixels.bottom; |
- float xAdjust = 0; |
- float yAdjust = 0; |
- |
- if (rectScreen.right - rectScreen.left < mRenderData.screenWidth) { |
- // Image is narrower than the screen, so center it. |
- xAdjust = -(rightDelta + leftDelta) / 2; |
- } else if (leftDelta > 0 && rightDelta > 0) { |
- // Panning the image left will show more of it. |
- xAdjust = -Math.min(leftDelta, rightDelta); |
- } else if (leftDelta < 0 && rightDelta < 0) { |
- // Pan the image right. |
- xAdjust = Math.min(-leftDelta, -rightDelta); |
- } |
- |
- // Apply similar logic for yAdjust. |
- if (rectScreen.bottom - rectScreen.top < mRenderData.screenHeight) { |
- yAdjust = -(bottomDelta + topDelta) / 2; |
- } else if (topDelta > 0 && bottomDelta > 0) { |
- yAdjust = -Math.min(topDelta, bottomDelta); |
- } else if (topDelta < 0 && bottomDelta < 0) { |
- yAdjust = Math.min(-topDelta, -bottomDelta); |
- } |
- |
- mRenderData.transform.postTranslate(xAdjust, yAdjust); |
- |
- mRenderStub.setTransformation(mRenderData.transform); |
} |
/** |
@@ -208,15 +136,21 @@ public class DesktopCanvas { |
* maximum zoom level is set arbitrarily, so that the user can zoom out again in a reasonable |
* time, and to prevent arithmetic overflow problems from displaying the image. |
* |
- * @param centerViewport Determines whether the viewport will be translated to the desired |
+ * @param scaleFactor The factor used to zoom the canvas in or out. |
+ * @param px The center x coordinate for the scale action. |
+ * @param py The center y coordinate for the scale action. |
+ * @param centerOnCursor Determines whether the viewport will be translated to the desired |
* center position before being adjusted to fit the screen boundaries. |
*/ |
- public void repositionImageWithZoom(boolean centerViewport) { |
+ public void scaleAndRepositionImage( |
+ float scaleFactor, float px, float py, boolean centerOnCursor) { |
// Avoid division by zero in case this gets called before the image size is initialized. |
if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { |
return; |
} |
+ mRenderData.transform.postScale(scaleFactor, scaleFactor, px, py); |
+ |
// Zoom out if the zoom level is too high. |
float currentZoomLevel = mRenderData.transform.mapRadius(1.0f); |
if (currentZoomLevel > MAX_ZOOM_FACTOR) { |
@@ -235,6 +169,87 @@ public class DesktopCanvas { |
mRenderData.transform.setScale(scale, scale); |
} |
- repositionImage(centerViewport); |
+ if (centerOnCursor) { |
+ setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
+ } else { |
+ // Find the new screen center (it was probably changed during the zoom operation) and |
+ // update the viewport and cursor. |
+ float[] mappedPoints = { |
+ ((float) mRenderData.screenWidth / 2), ((float) mRenderData.screenHeight / 2)}; |
+ Matrix screenToImage = new Matrix(); |
+ mRenderData.transform.invert(screenToImage); |
+ screenToImage.mapPoints(mappedPoints); |
+ // The cursor is mapped to the center of the viewport in this case. |
+ setCursorPosition(mappedPoints[0], mappedPoints[1]); |
+ } |
+ } |
+ |
+ /** |
+ * Repositions the image by translating it (without affecting the zoom level). |
+ */ |
+ private void repositionImage() { |
+ // Map the current viewport position to screen coordinates and adjust the image position. |
+ float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; |
+ mRenderData.transform.mapPoints(viewportPosition); |
+ |
+ float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportPosition[0]; |
+ float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewportPosition[1]; |
+ |
+ // Translate the image so the viewport center is displayed in the middle of the screen. |
+ mRenderData.transform.postTranslate(viewportTransX, viewportTransY); |
+ |
+ mRenderStub.setTransformation(mRenderData.transform); |
+ } |
+ |
+ /** |
+ * Updates the given point such that it refers to a coordinate within the bounds provided. |
+ * |
+ * @param point The point to adjust, the original object is modified. |
+ * @param bounds The bounds used to constrain the point. |
+ */ |
+ private void constrainPointToBounds(PointF point, RectF bounds) { |
+ if (point.x < bounds.left) { |
+ point.x = bounds.left; |
+ } else if (point.x > bounds.right) { |
+ point.x = bounds.right; |
+ } |
+ |
+ if (point.y < bounds.top) { |
+ point.y = bounds.top; |
+ } else if (point.y > bounds.bottom) { |
+ point.y = bounds.bottom; |
+ } |
+ } |
+ |
+ /** Returns a region which defines the set of valid cursor values in image space. */ |
+ private RectF getImageBounds() { |
+ return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); |
+ } |
+ |
+ /** 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}; |
+ 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 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; |
+ } |
+ |
+ // 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); |
} |
} |