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

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 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) inclu ding adjustments.
Lambros 2016/10/25 19:26:31 Clarify 'adjustments' by referring to {@link #adju
joedow 2016/10/25 20:12:06 Done.
238 */
239 private PointF getViewportCenter() {
Lambros 2016/10/25 19:26:31 I think this name is misleading, because this is i
joedow 2016/10/25 20:12:06 Done.
240 return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreen Height() / 2);
241 }
242
243 /**
246 * Repositions the image by translating it (without affecting the zoom level ). 244 * Repositions the image by translating it (without affecting the zoom level ).
247 */ 245 */
248 private void repositionImage() { 246 private void repositionImage() {
249 // Map the current viewport position to screen coordinates and adjust th e image position. 247 PointF viewportPosition = new PointF(mDesiredCenter.x, mDesiredCenter.y) ;
250 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; 248 constrainPointToBounds(viewportPosition, getViewportBounds());
251 mRenderData.transform.mapPoints(viewportPosition); 249 float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y};
250 mRenderData.transform.mapPoints(viewportAdjustment);
252 251
253 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; 252 // Adjust the viewport to include the overpan amount.
254 float viewportTransY = 253 viewportAdjustment[0] += mViewportOffset.x;
255 ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - m CursorOffsetScreenY; 254 viewportAdjustment[1] += mViewportOffset.y;
256 255
257 // Translate the image to move the viewport to the expected screen locat ion. 256 // Translate the image to move the viewport to the expected screen locat ion.
258 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); 257 PointF viewportCenter = getViewportCenter();
259 258 mRenderData.transform.postTranslate(
260 updateVisibleImagePadding(); 259 viewportCenter.x - viewportAdjustment[0], viewportCenter.y - vie wportAdjustment[1]);
261 260
262 mRenderStub.setTransformation(mRenderData.transform); 261 mRenderStub.setTransformation(mRenderData.transform);
263 } 262 }
264 263
265 /** 264 /**
266 * Updates the given point such that it refers to a coordinate within the bo unds provided. 265 * Updates the given point such that it refers to a coordinate within the bo unds provided.
267 * 266 *
268 * @param point The point to adjust, the original object is modified. 267 * @param point The point to adjust, the original object is modified.
269 * @param bounds The bounds used to constrain the point. 268 * @param bounds The bounds used to constrain the point.
270 */ 269 */
271 private void constrainPointToBounds(PointF point, RectF bounds) { 270 private void constrainPointToBounds(PointF point, RectF bounds) {
272 if (point.x < bounds.left) { 271 if (point.x < bounds.left) {
273 point.x = bounds.left; 272 point.x = bounds.left;
274 } else if (point.x > bounds.right) { 273 } else if (point.x > bounds.right) {
275 point.x = bounds.right; 274 point.x = bounds.right;
276 } 275 }
277 276
278 if (point.y < bounds.top) { 277 if (point.y < bounds.top) {
279 point.y = bounds.top; 278 point.y = bounds.top;
280 } else if (point.y > bounds.bottom) { 279 } else if (point.y > bounds.bottom) {
281 point.y = bounds.bottom; 280 point.y = bounds.bottom;
282 } 281 }
283 } 282 }
284 283
285 /** Returns a region which defines the set of valid cursor positions in imag e space. */ 284 /** Returns a region which defines the set of valid cursor positions in imag e space. */
286 private RectF getImageBounds() { 285 private RectF getImageBounds() {
287 // The set of valid cursor positions includes any point on the image as well as the padding. 286 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 } 287 }
309 288
310 /** Returns a region which defines the set of valid viewport center values i n image space. */ 289 /** Returns a region which defines the set of valid viewport center values i n image space. */
311 private RectF getViewportBounds() { 290 private RectF getViewportBounds() {
312 // The region of allowable viewport values is the imageBound rect, inset by the size of the 291 // 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 292 // 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. 293 // allowing the user to see and interact with all pixels one the desktop image.
315 Matrix screenToImage = new Matrix(); 294 Matrix screenToImage = new Matrix();
316 mRenderData.transform.invert(screenToImage); 295 mRenderData.transform.invert(screenToImage);
317 296
318 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; 297 PointF viewportCenter = getViewportCenter();
298 float[] screenVectors = {viewportCenter.x, viewportCenter.y};
319 screenToImage.mapVectors(screenVectors); 299 screenToImage.mapVectors(screenVectors);
320 300
321 PointF letterboxPadding = getLetterboxPadding(); 301 PointF letterboxPadding = getLetterboxPadding();
322 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; 302 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y};
323 screenToImage.mapVectors(letterboxPaddingVectors); 303 screenToImage.mapVectors(letterboxPaddingVectors);
324 304
325 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. 305 // 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]; 306 float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0];
327 float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVector s[1]; 307 float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1];
328 RectF imageBounds = getImageBounds(); 308 RectF imageBounds = getImageBounds();
329 imageBounds.inset(screenCenterX, screenCenterY); 309 imageBounds.inset(screenCenterX, screenCenterY);
330 return imageBounds; 310 return imageBounds;
331 } 311 }
332 312
333 /** 313 /**
314 * Returns a region defining the maximum offset distance required to view th e entire image
315 * given the current amount of System UI overlapping it.
316 */
317 private RectF getViewportOffsetBounds() {
318 // The allowable region is determined by:
319 // - Overlap of the System UI and image content
320 // - Current viewport offset
321 //
322 // The System UI overlap represents the maximum allowable offset, this i s used to bound the
323 // viewport movement in each direction. The current offset is used to p revent 'snapping'
324 // the image when the System UI overlap is reduced.
325 RectF viewportOffsetRect = new RectF();
326 viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y);
327
328 RectF systemUiOverlap = getSystemUiOverlap();
329 return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left ),
330 Math.min(viewportOffsetRect.top, -systemUiOverlap.top),
331 Math.max(viewportOffsetRect.right, systemUiOverlap.right),
332 Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom));
333 }
334
335 /**
334 * Provides the amount of padding needed to center the image content on the screen. 336 * Provides the amount of padding needed to center the image content on the screen.
335 */ 337 */
336 private PointF getLetterboxPadding() { 338 private PointF getLetterboxPadding() {
337 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; 339 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ;
338 mRenderData.transform.mapVectors(imageVectors); 340 mRenderData.transform.mapVectors(imageVectors);
339 341
340 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. 342 // 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. 343 // Since we center the image, split the difference so it is equally dist ributed.
342 float widthAdjust = 344 float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVec tors[0]) / 2, 0);
343 Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2 .0f, 0.0f); 345 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 346
347 return new PointF(widthAdjust, heightAdjust); 347 return new PointF(widthAdjust, heightAdjust);
348 } 348 }
349 349
350 /** 350 /**
351 * Returns the amount of System UI along each edge of the screen which could overlap the remote 351 * 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. 352 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value.
353 */ 353 */
354 private RectF getSystemUiOverlap() { 354 private RectF getSystemUiOverlap() {
355 // letterBox padding represents the space added to the image to center i t on the screen. 355 if (mSystemUiScreenRect.isEmpty()) {
356 return new RectF();
357 }
358
359 // LetterBox padding represents the space added to the image to center i t on the screen.
Lambros 2016/10/25 19:26:31 nit: lower-case B.
joedow 2016/10/25 20:12:06 Done.
356 // Since it does not contain any interactable UI, we ignore it when calc ulating the overlap 360 // 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. 361 // between the System UI and the remote desktop image.
358 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. 362 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists.
363 float adjustedScreenHeight = getAdjustedScreenHeight();
359 PointF letterboxPadding = getLetterboxPadding(); 364 PointF letterboxPadding = getLetterboxPadding();
360 return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), 365 return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f),
361 Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorO ffsetScreenY, 0.0f), 366 Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f),
362 Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), 367 Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - l etterboxPadding.x,
363 Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCurs orOffsetScreenY, 368 0.0f),
369 Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - let terboxPadding.y,
364 0.0f)); 370 0.0f));
365 } 371 }
366 372
367 /** 373 /**
368 * Calculates the amount of padding visible on each edge of the desktop imag e. 374 * Applies the given offset, as needed, to allow moving the image outside it s normal bounds.
369 */ 375 */
370 private void updateVisibleImagePadding() { 376 private void calculateViewportOffset(float offsetX, float offsetY) {
371 PointF letterboxPadding = getLetterboxPadding(); 377 if (mSystemUiScreenRect.isEmpty()) {
372 float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.i mageHeight}; 378 // We only want to directly change the viewport offset when System U I is present.
373 mRenderData.transform.mapPoints(imagePoints); 379 return;
380 }
374 381
375 mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0 .0f), 382 float[] offsets = {offsetX, offsetY};
376 Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), 383 mRenderData.transform.mapVectors(offsets);
377 Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPad ding.x, 0.0f), 384
378 Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPa dding.y, 0.0f)); 385 // Use a temporary variable here as {@link #getViewportOffsetBounds()} u ses the current
386 // viewport offset as a maximum boundary. If we add the offset first, t he result ends up
387 // being unbounded. Thus we use a temporary object for the boundary cal culation.
388 PointF requestedOffset =
389 new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + o ffsets[1]);
390 constrainPointToBounds(requestedOffset, getViewportOffsetBounds());
391 mViewportOffset.set(requestedOffset);
392 }
393
394 /**
395 * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is
396 * already running or the offset is already 0.
397 */
398 private void startOffsetReductionAnimation() {
399 if (mFrameRenderedCallback != null || Math.abs(mViewportOffset.length()) < EPSILON) {
Lambros 2016/10/25 19:26:31 Math.abs() is unnecessary - length() is never nega
joedow 2016/10/25 20:12:06 Good point!
400 return;
401 }
402
403 mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() {
404 private static final float DURATION_MS = 250.0f;
405
406 private final Interpolator mInterpolator = new DecelerateInterpolato r();
407
408 private long mStartTime = 0;
409 private float mOriginalX = 0.0f;
410 private float mOriginalY = 0.0f;
411
412 @Override
413 public Boolean run(Void p) {
414 if (mFrameRenderedCallback == null) {
415 return false;
416 }
417
418 if (mStartTime == 0) {
419 mStartTime = SystemClock.elapsedRealtime();
420 mOriginalX = mViewportOffset.x;
421 mOriginalY = mViewportOffset.y;
422 }
423
424 float progress = (SystemClock.elapsedRealtime() - mStartTime) / DURATION_MS;
425 if (progress < 1.0f) {
426 float reductionFactor = 1.0f - mInterpolator.getInterpolatio n(progress);
427 mViewportOffset.set(mOriginalX * reductionFactor, mOriginalY * reductionFactor);
428 } else {
429 mViewportOffset.set(0.0f, 0.0f);
430 mFrameRenderedCallback = null;
431 }
432
433 repositionImage();
434
435 return mFrameRenderedCallback != null;
436 }
437 };
438
439 mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback);
440 }
441
442 /**
443 * Stops an existing offset reduction animation.
444 */
445 private void stopOffsetReductionAnimation() {
446 // Setting this value this null will prevent it from continuing to execu te.
447 mFrameRenderedCallback = null;
379 } 448 }
380 } 449 }
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