Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(201)

Side by Side Diff: remoting/android/java/src/org/chromium/chromoting/DesktopCanvas.java

Issue 2441723004: [Android][Client] Updating the calculations used for image overpanning (Closed)
Patch Set: Addressing feedback and merging with ToT Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chromoting; 5 package org.chromium.chromoting;
6 6
7 import android.graphics.Matrix; 7 import android.graphics.Matrix;
8 import android.graphics.PointF; 8 import android.graphics.PointF;
9 import android.graphics.Rect; 9 import android.graphics.Rect;
10 import android.graphics.RectF; 10 import android.graphics.RectF;
11 import android.os.SystemClock;
12 import android.view.animation.DecelerateInterpolator;
13 import android.view.animation.Interpolator;
11 14
12 /** 15 /**
13 * This class is responsible for transforming the desktop image matrix. 16 * This class is responsible for transforming the desktop image matrix.
14 */ 17 */
15 public class DesktopCanvas { 18 public class DesktopCanvas {
16 /** 19 /** Used for floating point comparisons. */
17 * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. 20 private static final float EPSILON = 0.0001f;
18 */ 21
22 /** Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. */
19 private static final float MAX_ZOOM_FACTOR = 100.0f; 23 private static final float MAX_ZOOM_FACTOR = 100.0f;
20 24
21 /**
22 * Used to smoothly reduce the amount of padding while the user is zooming.
23 */
24 private static final float PADDING_REDUCTION_FACTOR = 0.85f;
25
26 private final RenderStub mRenderStub; 25 private final RenderStub mRenderStub;
27 private final RenderData mRenderData; 26 private final RenderData mRenderData;
28 27
29 /** 28 /**
30 * Represents the actual center of the viewport in image space. This value needs to be a pair
31 * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning
32 * animations at high zoom levels.
33 */
34 // TODO(joedow): See if we can collapse Viewport and Cursor position members . They were needed
35 // in the past due to how we calculated the center positions but may not be needed now.
36 private PointF mViewportPosition = new PointF();
37
38 /**
39 * Represents the desired center of the viewport in image space. This value may not represent 29 * Represents the desired center of the viewport in image space. This value may not represent
40 * the actual center of the viewport as adjustments are made to ensure as mu ch of the desktop is 30 * the actual center of the viewport as adjustments are made to ensure as mu ch of the desktop is
41 * visible as possible. This value needs to be a pair of floats so the desk top image can be 31 * visible as possible. This value needs to be a pair of floats so the desk top image can be
42 * positioned with sub-pixel accuracy for smoother panning animations at hig h zoom levels. 32 * positioned with sub-pixel accuracy for smoother panning animations at hig h zoom levels.
43 * Note: The internal cursor position may be placed outside of the image bou ndary, however the
44 * cursor position we inject on the host side is restricted to the actual im age dimensions.
45 */ 33 */
46 private PointF mCursorPosition = new PointF(); 34 private PointF mDesiredCenter = new PointF();
47 35
48 /** 36 /**
49 * Represents the amount of space, in pixels, used by System UI on each edge of the screen. 37 * If System UI exists, this contains the area of the screen which is unobsc ured by it,
38 * otherwise it is empty.
50 */ 39 */
51 // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets. 40 private Rect mSystemUiScreenRect = new Rect();
52 private Rect mSystemUiScreenSize = new Rect();
53 41
54 /** 42 /**
55 * Represents the amount of padding, in screen pixels, added to each edge of the desktop image. 43 * Represents the amount of space, in pixels, to shift the image under the v iewport. This value
56 * This extra space allows the user to reveal portions of the desktop image which are obscured 44 * is used to allow panning the image further than would be possible when us ing the normal
57 * by System UI. 45 * boundary values to account for System UI. This functionality ensures the user can view and
46 * interact with any area of the remote image, even when System UI might oth erwise obscure it.
58 */ 47 */
59 private RectF mVisibleImagePadding = new RectF(); 48 private PointF mViewportOffset = new PointF();
60 49
61 /** 50 /**
62 * Tracks whether to adjust the viewport to account for System UI. If false, the viewport 51 * Tracks whether to adjust the size of the viewport to account for System U I. If false, the
63 * center is the center of the screen. If true, then System UI offsets will be used to 52 * viewport center is mapped to the center of the screen. If true, then Sys tem UI sizes will be
64 * adjust the position of the viewport to ensure the cursor is visible. 53 * used to determine the center of the viewport.
65 */ 54 */
66 private boolean mAdjustViewportForSystemUi = false; 55 private boolean mAdjustViewportSizeForSystemUi = false;
67 56
68 /** 57 /* Used to perform per-frame rendering tasks. */
69 * Represents the amount of space, in pixels, to adjust the cursor center alo ng the y-axis. 58 private Event.ParameterCallback<Boolean, Void> mFrameRenderedCallback;
70 */
71 private float mCursorOffsetScreenY = 0.0f;
72 59
73 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { 60 public DesktopCanvas(RenderStub renderStub, RenderData renderData) {
74 mRenderStub = renderStub; 61 mRenderStub = renderStub;
75 mRenderData = renderData; 62 mRenderData = renderData;
76 } 63 }
77 64
78 /** 65 /**
79 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures 66 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion).
80 * the viewport is updated to include the cursor within it.
81 * 67 *
82 * @param newX The new x coordinate value for the desired center position. 68 * @param newX The new x coordinate value for the desired center position.
83 * @param newY The new y coordinate value for the desired center position. 69 * @param newY The new y coordinate value for the desired center position.
84 */ 70 */
85 public void setCursorPosition(float newX, float newY) { 71 public void setCursorPosition(float newX, float newY) {
86 // First set the cursor position since its potential values are a supers et of the viewport. 72 updateCursorPosition(newX, newY, getImageBounds());
87 mCursorPosition.set(newX, newY); 73 }
88 constrainPointToBounds(mCursorPosition, getImageBounds());
89 74
90 // Now set the viewport position based on the cursor. 75 /**
91 mViewportPosition.set(mCursorPosition); 76 * Sets the center of the viewport using an absolute position (in image coor dinates).
92 constrainPointToBounds(mViewportPosition, getViewportBounds()); 77 *
78 * @param newX The new x coordinate value for the center position of the vie wport.
79 * @param newY The new y coordinate value for the center position of the vie wport.
80 */
81 public void setViewportCenter(float newX, float newY) {
82 updateCursorPosition(newX, newY, getViewportBounds());
83 }
93 84
94 repositionImage(); 85 /**
86 * Shifts the cursor by the passed in values (in image coordinates).
87 *
88 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis.
89 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis.
90 * @return A point representing the new cursor position.
91 */
92 public PointF moveCursorPosition(float deltaX, float deltaY) {
93 updateCursorPosition(
94 mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getImageBo unds());
95 return new PointF(mDesiredCenter.x, mDesiredCenter.y);
95 } 96 }
96 97
97 /** 98 /**
98 * Shifts the viewport by the passed in values (in image coordinates). 99 * Shifts the viewport by the passed in values (in image coordinates).
99 * 100 *
100 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. 101 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis.
101 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. 102 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis.
102 */ 103 */
103 public void moveViewportCenter(float deltaX, float deltaY) { 104 public void moveViewportCenter(float deltaX, float deltaY) {
104 // Offset and adjust the viewport center position to fit the screen. 105 updateCursorPosition(
105 mViewportPosition.offset(deltaX, deltaY); 106 mDesiredCenter.x + deltaX, mDesiredCenter.y + deltaY, getViewpor tBounds());
106 constrainPointToBounds(mViewportPosition, getViewportBounds());
107
108 // We don't need to constrain the cursor position as the viewport positi on is always within
109 // the bounds of the potential cursor positions.
110 mCursorPosition.set(mViewportPosition);
111
112 repositionImage();
113 } 107 }
114 108
115 /** 109 /**
116 * Shifts the cursor by the passed in values (in image coordinates) and adju sts the viewport.
117 *
118 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis.
119 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis.
120 * @return A point representing the new cursor position.
121 */
122 public PointF moveCursorPosition(float deltaX, float deltaY) {
123 setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY );
124 return new PointF(mCursorPosition.x, mCursorPosition.y);
125 }
126
127 /**
128 * Handles System UI size and visibility changes. 110 * Handles System UI size and visibility changes.
129 * 111 *
130 * @param parameter The set of values defining the current System UI state. 112 * @param parameter The set of values defining the current System UI state.
131 */ 113 */
132 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) { 114 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) {
133 if (parameter.softInputMethodVisible) { 115 if (parameter.softInputMethodVisible) {
134 mSystemUiScreenSize.set(parameter.left, parameter.top, 116 mSystemUiScreenRect.set(
135 mRenderData.screenWidth - parameter.right, 117 parameter.left, parameter.top, parameter.right, parameter.bo ttom);
136 mRenderData.screenHeight - parameter.bottom);
137 118
138 if (mAdjustViewportForSystemUi) { 119 stopOffsetReductionAnimation();
139 // Adjust the cursor position to ensure it's visible when large System UI (1/3 or 120 } else {
140 // more of the total screen size) is displayed (typically the So ft Keyboard). 121 mSystemUiScreenRect.setEmpty();
141 // Without this change, it is difficult for users to enter text into edit controls 122 startOffsetReductionAnimation();
142 // which are located bottom of the screen and may not see the cu rsor at all. 123 }
143 if (mSystemUiScreenSize.bottom > (mRenderData.screenHeight / 3)) {
144 // Center the cursor within the viewable area (not obscured by System UI).
145 mCursorOffsetScreenY = (float) parameter.bottom / 2.0f;
146 } else {
147 mCursorOffsetScreenY = 0.0f;
148 }
149 124
150 // Apply the cursor offset. 125 repositionImage();
151 setCursorPosition(mCursorPosition.x, mCursorPosition.y);
152 }
153 } else {
154 mCursorOffsetScreenY = 0.0f;
155 mSystemUiScreenSize.setEmpty();
156 }
157 } 126 }
158 127
159 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { 128 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) {
160 mAdjustViewportForSystemUi = adjustViewportForSystemUi; 129 mAdjustViewportSizeForSystemUi = adjustViewportForSystemUi;
130
131 // The viewport center may have changed so reposition the image to refle ct the new value.
132 repositionImage();
161 } 133 }
162 134
163 /** Resizes the image by zooming it such that the image is displayed without borders. */ 135 /** Resizes the image by zooming it such that the image is displayed without borders. */
164 public void resizeImageToFitScreen() { 136 public void resizeImageToFitScreen() {
165 // Protect against being called before the image has been initialized. 137 // Protect against being called before the image has been initialized.
166 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { 138 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) {
167 return; 139 return;
168 } 140 }
169 141
170 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; 142 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth;
171 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; 143 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height;
172 float screenToImageScale = Math.max(widthRatio, heightRatio); 144 float screenToImageScale = Math.max(widthRatio, heightRatio);
173 145
174 // If the image is smaller than the screen in either dimension, then we want to scale it 146 // If the image is smaller than the screen in either dimension, then we want to scale it
175 // up to fit both and fill the screen with the image of the remote deskt op. 147 // up to fit both and fill the screen with the image of the remote deskt op.
176 if (screenToImageScale > 1.0f) { 148 if (screenToImageScale > 1.0f) {
177 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); 149 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e);
178 } 150 }
179 } 151 }
180 152
181 /** 153 /**
182 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible 154 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible
183 * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The 155 * limits. The minimum zoom level is chosen to avoid letterboxing on all 4 s ides. The
184 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable 156 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable
185 * time, and to prevent arithmetic overflow problems from displaying the ima ge. 157 * time, and to prevent arithmetic overflow problems from displaying the ima ge.
186 * 158 *
187 * @param scaleFactor The factor used to zoom the canvas in or out. 159 * @param scaleFactor The factor used to zoom the canvas in or out.
188 * @param px The center x coordinate for the scale action. 160 * @param px The center x coordinate for the scale action.
189 * @param py The center y coordinate for the scale action. 161 * @param py The center y coordinate for the scale action.
190 * @param centerOnCursor Determines whether the viewport will be translated to the desired 162 * @param centerOnCursor Determines whether the viewport will be translated to the desired
191 * center position before being adjusted to fit the sc reen boundaries. 163 * center position before being adjusted to fit the sc reen boundaries.
192 */ 164 */
193 public void scaleAndRepositionImage( 165 public void scaleAndRepositionImage(
(...skipping 16 matching lines...) Expand all
210 mRenderData.transform.mapVectors(imageSize); 182 mRenderData.transform.mapVectors(imageSize);
211 183
212 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { 184 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) {
213 // Displayed image is too small in both directions, so apply the min imum zoom 185 // Displayed image is too small in both directions, so apply the min imum zoom
214 // level needed to fit either the width or height. 186 // level needed to fit either the width or height.
215 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, 187 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth,
216 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); 188 (float) mRenderData.screenHeight / mRenderDat a.imageHeight);
217 mRenderData.transform.setScale(scale, scale); 189 mRenderData.transform.setScale(scale, scale);
218 } 190 }
219 191
220 // Trim the image padding if the user is zooming out. This prevents cas es where the image
221 // pops to the center when it reaches its minimum size. Note that we do not need to do
222 // anything when the user is zooming in as the canvas will expand and ab sorb the padding.
223 if (scaleFactor < 1.0f) {
224 mVisibleImagePadding.set(mVisibleImagePadding.left * PADDING_REDUCTI ON_FACTOR,
225 mVisibleImagePadding.top * PADDING_REDUCTION_FACTOR,
226 mVisibleImagePadding.right * PADDING_REDUCTION_FACTOR,
227 mVisibleImagePadding.bottom * PADDING_REDUCTION_FACTOR);
228 }
229
230 if (centerOnCursor) { 192 if (centerOnCursor) {
231 setCursorPosition(mCursorPosition.x, mCursorPosition.y); 193 setCursorPosition(mDesiredCenter.x, mDesiredCenter.y);
232 } else { 194 } else {
233 // Find the new screen center (it was probably changed during the zo om operation) and 195 // Find the new screen center (it probably changed during the zoom o peration) and update
234 // update the viewport and cursor. 196 // the viewport to smoothly track the zoom gesture.
235 float[] mappedPoints = { 197 float[] mappedPoints = {((float) mRenderData.screenWidth / 2) - mVie wportOffset.x,
236 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)}; 198 ((float) mRenderData.screenHeight / 2) - mViewportOffset.y};
237 Matrix screenToImage = new Matrix(); 199 Matrix screenToImage = new Matrix();
238 mRenderData.transform.invert(screenToImage); 200 mRenderData.transform.invert(screenToImage);
239 screenToImage.mapPoints(mappedPoints); 201 screenToImage.mapPoints(mappedPoints);
240 // The cursor is mapped to the center of the viewport in this case. 202 // The cursor is mapped to the center of the viewport in this case.
241 setCursorPosition(mappedPoints[0], mappedPoints[1]); 203 setViewportCenter(mappedPoints[0], mappedPoints[1]);
242 } 204 }
243 } 205 }
244 206
245 /** 207 /**
208 * Sets the new cursor position, bounded by the given rect, and updates the image transform to
209 * reflect the new position.
210 */
211 private void updateCursorPosition(float newX, float newY, RectF bounds) {
212 mDesiredCenter.set(newX, newY);
213 constrainPointToBounds(mDesiredCenter, bounds);
214
215 calculateViewportOffset(newX - mDesiredCenter.x, newY - mDesiredCenter.y );
216
217 repositionImage();
218 }
219
220 /**
221 * Returns the height of the screen (in screen coordinates) for use in calcu lations involving
222 * viewport positioning.
223 */
224 private float getAdjustedScreenHeight() {
225 float adjustedScreenHeight;
226 if (mAdjustViewportSizeForSystemUi && !mSystemUiScreenRect.isEmpty()) {
227 // Find the center point of the viewport on the screen.
228 adjustedScreenHeight = mSystemUiScreenRect.bottom;
229 } else {
230 adjustedScreenHeight = ((float) mRenderData.screenHeight);
231 }
232
233 return adjustedScreenHeight;
234 }
235
236 /**
237 * Returns the center position of the viewport (in screen coordinates) takin g System UI into
238 * account.
239 */
240 private PointF getViewportScreenCenter() {
241 return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreen Height() / 2);
242 }
243
244 /**
246 * Repositions the image by translating it (without affecting the zoom level ). 245 * Repositions the image by translating it (without affecting the zoom level ).
247 */ 246 */
248 private void repositionImage() { 247 private void repositionImage() {
249 // Map the current viewport position to screen coordinates and adjust th e image position. 248 PointF viewportPosition = new PointF(mDesiredCenter.x, mDesiredCenter.y) ;
250 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; 249 constrainPointToBounds(viewportPosition, getViewportBounds());
251 mRenderData.transform.mapPoints(viewportPosition); 250 float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y};
251 mRenderData.transform.mapPoints(viewportAdjustment);
252 252
253 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; 253 // Adjust the viewport to include the overpan amount.
254 float viewportTransY = 254 viewportAdjustment[0] += mViewportOffset.x;
255 ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - m CursorOffsetScreenY; 255 viewportAdjustment[1] += mViewportOffset.y;
256 256
257 // Translate the image to move the viewport to the expected screen locat ion. 257 // Translate the image to move the viewport to the expected screen locat ion.
258 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); 258 PointF viewportCenter = getViewportScreenCenter();
259 259 mRenderData.transform.postTranslate(
260 updateVisibleImagePadding(); 260 viewportCenter.x - viewportAdjustment[0], viewportCenter.y - vie wportAdjustment[1]);
261 261
262 mRenderStub.setTransformation(mRenderData.transform); 262 mRenderStub.setTransformation(mRenderData.transform);
263 } 263 }
264 264
265 /** 265 /**
266 * Updates the given point such that it refers to a coordinate within the bo unds provided. 266 * Updates the given point such that it refers to a coordinate within the bo unds provided.
267 * 267 *
268 * @param point The point to adjust, the original object is modified. 268 * @param point The point to adjust, the original object is modified.
269 * @param bounds The bounds used to constrain the point. 269 * @param bounds The bounds used to constrain the point.
270 */ 270 */
271 private void constrainPointToBounds(PointF point, RectF bounds) { 271 private void constrainPointToBounds(PointF point, RectF bounds) {
272 if (point.x < bounds.left) { 272 if (point.x < bounds.left) {
273 point.x = bounds.left; 273 point.x = bounds.left;
274 } else if (point.x > bounds.right) { 274 } else if (point.x > bounds.right) {
275 point.x = bounds.right; 275 point.x = bounds.right;
276 } 276 }
277 277
278 if (point.y < bounds.top) { 278 if (point.y < bounds.top) {
279 point.y = bounds.top; 279 point.y = bounds.top;
280 } else if (point.y > bounds.bottom) { 280 } else if (point.y > bounds.bottom) {
281 point.y = bounds.bottom; 281 point.y = bounds.bottom;
282 } 282 }
283 } 283 }
284 284
285 /** Returns a region which defines the set of valid cursor positions in imag e space. */ 285 /** Returns a region which defines the set of valid cursor positions in imag e space. */
286 private RectF getImageBounds() { 286 private RectF getImageBounds() {
287 // The set of valid cursor positions includes any point on the image as well as the padding. 287 return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight);
288 // Padding is additional space added to the image which is the larger va lue of:
289 // - Potential overlap of the System UI and image content
290 // - Actual amount of padding already being used
291 //
292 // By expanding the area, we allow the user to move the cursor 'under' t he System UI which
293 // pulls the content out from under it and allows it to be visible. Onc e the System UI has
294 // been dismissed or changes size, we use the actual padding value inste ad which prevents
295 // the desktop image from 'snapping' back to pre-System UI state.
296 RectF systemUiOverlap = getSystemUiOverlap();
297 float[] padding = {Math.max(mVisibleImagePadding.left, systemUiOverlap.l eft),
298 Math.max(mVisibleImagePadding.top + mCursorOffsetScreenY, system UiOverlap.top),
299 Math.max(mVisibleImagePadding.right, systemUiOverlap.right),
300 Math.max(mVisibleImagePadding.bottom - mCursorOffsetScreenY,
301 systemUiOverlap.bottom)};
302 Matrix screenToImage = new Matrix();
303 mRenderData.transform.invert(screenToImage);
304 screenToImage.mapVectors(padding);
305
306 return new RectF(-padding[0], -padding[1], mRenderData.imageWidth + padd ing[2],
307 mRenderData.imageHeight + padding[3]);
308 } 288 }
309 289
310 /** Returns a region which defines the set of valid viewport center values i n image space. */ 290 /** Returns a region which defines the set of valid viewport center values i n image space. */
311 private RectF getViewportBounds() { 291 private RectF getViewportBounds() {
312 // The region of allowable viewport values is the imageBound rect, inset by the size of the 292 // The region of allowable viewport values is the imageBound rect, inset by the size of the
313 // viewport itself. This prevents over and under panning of the viewpor t while still 293 // viewport itself. This prevents over and under panning of the viewpor t while still
314 // allowing the user to see and interact with all pixels one the desktop image. 294 // allowing the user to see and interact with all pixels one the desktop image.
315 Matrix screenToImage = new Matrix(); 295 Matrix screenToImage = new Matrix();
316 mRenderData.transform.invert(screenToImage); 296 mRenderData.transform.invert(screenToImage);
317 297
318 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; 298 PointF viewportCenter = getViewportScreenCenter();
299 float[] screenVectors = {viewportCenter.x, viewportCenter.y};
319 screenToImage.mapVectors(screenVectors); 300 screenToImage.mapVectors(screenVectors);
320 301
321 PointF letterboxPadding = getLetterboxPadding(); 302 PointF letterboxPadding = getLetterboxPadding();
322 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; 303 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y};
323 screenToImage.mapVectors(letterboxPaddingVectors); 304 screenToImage.mapVectors(letterboxPaddingVectors);
324 305
325 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. 306 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space.
326 float screenCenterX = (screenVectors[0] / 2.0f) - letterboxPaddingVector s[0]; 307 float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0];
327 float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVector s[1]; 308 float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1];
328 RectF imageBounds = getImageBounds(); 309 RectF imageBounds = getImageBounds();
329 imageBounds.inset(screenCenterX, screenCenterY); 310 imageBounds.inset(screenCenterX, screenCenterY);
330 return imageBounds; 311 return imageBounds;
331 } 312 }
332 313
333 /** 314 /**
315 * Returns a region defining the maximum offset distance required to view th e entire image
316 * given the current amount of System UI overlapping it.
317 */
318 private RectF getViewportOffsetBounds() {
319 // The allowable region is determined by:
320 // - Overlap of the System UI and image content
321 // - Current viewport offset
322 //
323 // The System UI overlap represents the maximum allowable offset, this i s used to bound the
324 // viewport movement in each direction. The current offset is used to p revent 'snapping'
325 // the image when the System UI overlap is reduced.
326 RectF viewportOffsetRect = new RectF();
327 viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y);
328
329 RectF systemUiOverlap = getSystemUiOverlap();
330 return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left ),
331 Math.min(viewportOffsetRect.top, -systemUiOverlap.top),
332 Math.max(viewportOffsetRect.right, systemUiOverlap.right),
333 Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom));
334 }
335
336 /**
334 * Provides the amount of padding needed to center the image content on the screen. 337 * Provides the amount of padding needed to center the image content on the screen.
335 */ 338 */
336 private PointF getLetterboxPadding() { 339 private PointF getLetterboxPadding() {
337 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; 340 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ;
338 mRenderData.transform.mapVectors(imageVectors); 341 mRenderData.transform.mapVectors(imageVectors);
339 342
340 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. 343 // We want to letterbox when the image is smaller than the screen in a s pecific dimension.
341 // Since we center the image, split the difference so it is equally dist ributed. 344 // Since we center the image, split the difference so it is equally dist ributed.
342 float widthAdjust = 345 float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVec tors[0]) / 2, 0);
343 Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2 .0f, 0.0f); 346 float heightAdjust = Math.max((getAdjustedScreenHeight() - imageVectors[ 1]) / 2, 0);
344 float heightAdjust =
345 Math.max(((float) mRenderData.screenHeight - imageVectors[1]) / 2.0f, 0.0f);
346 347
347 return new PointF(widthAdjust, heightAdjust); 348 return new PointF(widthAdjust, heightAdjust);
348 } 349 }
349 350
350 /** 351 /**
351 * Returns the amount of System UI along each edge of the screen which could overlap the remote 352 * Returns the amount of System UI along each edge of the screen which could overlap the remote
352 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value. 353 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value.
353 */ 354 */
354 private RectF getSystemUiOverlap() { 355 private RectF getSystemUiOverlap() {
355 // letterBox padding represents the space added to the image to center i t on the screen. 356 if (mSystemUiScreenRect.isEmpty()) {
357 return new RectF();
358 }
359
360 // Letterbox padding represents the space added to the image to center i t on the screen.
356 // Since it does not contain any interactable UI, we ignore it when calc ulating the overlap 361 // Since it does not contain any interactable UI, we ignore it when calc ulating the overlap
357 // between the System UI and the remote desktop image. 362 // between the System UI and the remote desktop image.
358 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. 363 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists.
364 float adjustedScreenHeight = getAdjustedScreenHeight();
359 PointF letterboxPadding = getLetterboxPadding(); 365 PointF letterboxPadding = getLetterboxPadding();
360 return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), 366 return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f),
361 Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorO ffsetScreenY, 0.0f), 367 Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f),
362 Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), 368 Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - l etterboxPadding.x,
363 Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCurs orOffsetScreenY, 369 0.0f),
370 Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - let terboxPadding.y,
364 0.0f)); 371 0.0f));
365 } 372 }
366 373
367 /** 374 /**
368 * Calculates the amount of padding visible on each edge of the desktop imag e. 375 * Applies the given offset, as needed, to allow moving the image outside it s normal bounds.
369 */ 376 */
370 private void updateVisibleImagePadding() { 377 private void calculateViewportOffset(float offsetX, float offsetY) {
371 PointF letterboxPadding = getLetterboxPadding(); 378 if (mSystemUiScreenRect.isEmpty()) {
372 float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.i mageHeight}; 379 // We only want to directly change the viewport offset when System U I is present.
373 mRenderData.transform.mapPoints(imagePoints); 380 return;
381 }
374 382
375 mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0 .0f), 383 float[] offsets = {offsetX, offsetY};
376 Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), 384 mRenderData.transform.mapVectors(offsets);
377 Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPad ding.x, 0.0f), 385
378 Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPa dding.y, 0.0f)); 386 // Use a temporary variable here as {@link #getViewportOffsetBounds()} u ses the current
387 // viewport offset as a maximum boundary. If we add the offset first, t he result ends up
388 // being unbounded. Thus we use a temporary object for the boundary cal culation.
389 PointF requestedOffset =
390 new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + o ffsets[1]);
391 constrainPointToBounds(requestedOffset, getViewportOffsetBounds());
392 mViewportOffset.set(requestedOffset);
393 }
394
395 /**
396 * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is
397 * already running or the offset is already 0.
398 */
399 private void startOffsetReductionAnimation() {
400 if (mFrameRenderedCallback != null || mViewportOffset.length() < EPSILON ) {
401 return;
402 }
403
404 mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() {
405 private static final float DURATION_MS = 250.0f;
406
407 private final Interpolator mInterpolator = new DecelerateInterpolato r();
408
409 private long mStartTime = 0;
410 private float mOriginalX = 0.0f;
411 private float mOriginalY = 0.0f;
412
413 @Override
414 public Boolean run(Void p) {
415 if (mFrameRenderedCallback == null) {
416 return false;
417 }
418
419 if (mStartTime == 0) {
420 mStartTime = SystemClock.elapsedRealtime();
421 mOriginalX = mViewportOffset.x;
422 mOriginalY = mViewportOffset.y;
423 }
424
425 float progress = (SystemClock.elapsedRealtime() - mStartTime) / DURATION_MS;
426 if (progress < 1.0f) {
427 float reductionFactor = 1.0f - mInterpolator.getInterpolatio n(progress);
428 mViewportOffset.set(mOriginalX * reductionFactor, mOriginalY * reductionFactor);
429 } else {
430 mViewportOffset.set(0.0f, 0.0f);
431 mFrameRenderedCallback = null;
432 }
433
434 repositionImage();
435
436 return mFrameRenderedCallback != null;
437 }
438 };
439
440 mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback);
441 }
442
443 /**
444 * Stops an existing offset reduction animation.
445 */
446 private void stopOffsetReductionAnimation() {
447 // Setting this value this null will prevent it from continuing to execu te.
448 mFrameRenderedCallback = null;
379 } 449 }
380 } 450 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698