OLD | NEW |
---|---|
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 | 11 |
12 /** | 12 /** |
13 * This class is responsible for transforming the desktop image matrix. | 13 * This class is responsible for transforming the desktop image matrix. |
14 */ | 14 */ |
15 public class DesktopCanvas { | 15 public class DesktopCanvas { |
16 /** | 16 /** Used for floating point comparisons. */ |
17 * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. | 17 private static final float EPSILON = 0.0001f; |
18 */ | 18 |
19 /** Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. */ | |
19 private static final float MAX_ZOOM_FACTOR = 100.0f; | 20 private static final float MAX_ZOOM_FACTOR = 100.0f; |
20 | 21 |
21 /** | 22 /** Used to smoothly reduce the viewport offset. */ |
22 * Used to smoothly reduce the amount of padding while the user is zooming. | 23 private static final float VIEWPORT_OFFSET_REDUCTION_FACTOR = 0.85f; |
23 */ | 24 |
24 private static final float PADDING_REDUCTION_FACTOR = 0.85f; | 25 /** Used to terminate the viewport offset animation once this value has been reached. */ |
26 private static final float MINIMUM_VIEWPORT_OFFSET_PIXELS = 1; | |
25 | 27 |
26 private final RenderStub mRenderStub; | 28 private final RenderStub mRenderStub; |
27 private final RenderData mRenderData; | 29 private final RenderData mRenderData; |
28 | 30 |
29 /** | 31 /** |
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 | 32 * 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 | 33 * 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 | 34 * 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. | 35 * 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 */ | 36 */ |
46 private PointF mCursorPosition = new PointF(); | 37 private PointF mCursorPosition = new PointF(); |
Yuwei
2016/10/25 08:25:26
Maybe not for this CL. If this is the desired cent
joedow
2016/10/25 16:49:50
Lambros and I have talked about this a bit already
| |
47 | 38 |
48 /** | 39 /** |
49 * Represents the amount of space, in pixels, used by System UI on each edge of the screen. | 40 * If System UI exists, this contains the area of the screen which is unobsc ured by it, |
41 * otherwise it is empty. | |
50 */ | 42 */ |
51 // TODO(joedow): Update usage of this member so it is a true Rect instead of a set of offsets. | 43 private Rect mSystemUiScreenRect = new Rect(); |
52 private Rect mSystemUiScreenSize = new Rect(); | |
53 | 44 |
54 /** | 45 /** |
55 * Represents the amount of padding, in screen pixels, added to each edge of the desktop image. | 46 * 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 | 47 * is used to allow panning the image further than would be possible when us ing the normal |
57 * by System UI. | 48 * boundary values to account for System UI. This functionality ensures the user can view and |
49 * interact with any area of the remote image, even when System UI might oth erwise obscure it. | |
58 */ | 50 */ |
59 private RectF mVisibleImagePadding = new RectF(); | 51 private PointF mViewportOffset = new PointF(); |
60 | 52 |
61 /** | 53 /** |
62 * Tracks whether to adjust the viewport to account for System UI. If false, the viewport | 54 * 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 | 55 * 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. | 56 * used to determine the center of the viewport. |
65 */ | 57 */ |
66 private boolean mAdjustViewportForSystemUi = false; | 58 private boolean mAdjustViewportSizeForSystemUi = false; |
67 | 59 |
68 /** | 60 /* 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. | 61 private Event.ParameterCallback<Boolean, Void> mFrameRenderedCallback; |
70 */ | |
71 private float mCursorOffsetScreenY = 0.0f; | |
72 | 62 |
73 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { | 63 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
74 mRenderStub = renderStub; | 64 mRenderStub = renderStub; |
75 mRenderData = renderData; | 65 mRenderData = renderData; |
76 } | 66 } |
77 | 67 |
78 /** | 68 /** |
79 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures | 69 * 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 * | 70 * |
82 * @param newX The new x coordinate value for the desired center position. | 71 * @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. | 72 * @param newY The new y coordinate value for the desired center position. |
84 */ | 73 */ |
85 public void setCursorPosition(float newX, float newY) { | 74 public void setCursorPosition(float newX, float newY) { |
86 // First set the cursor position since its potential values are a supers et of the viewport. | 75 updateCursorPosition(newX, newY, getImageBounds()); |
87 mCursorPosition.set(newX, newY); | 76 } |
88 constrainPointToBounds(mCursorPosition, getImageBounds()); | |
89 | 77 |
90 // Now set the viewport position based on the cursor. | 78 /** |
91 mViewportPosition.set(mCursorPosition); | 79 * Sets the center of the viewport using an absolute position (in image coor dinates). |
92 constrainPointToBounds(mViewportPosition, getViewportBounds()); | 80 * |
81 * @param newX The new x coordinate value for the center position of the vie wport. | |
82 * @param newY The new y coordinate value for the center position of the vie wport. | |
83 */ | |
84 public void setViewportCenter(float newX, float newY) { | |
85 updateCursorPosition(newX, newY, getViewportBounds()); | |
86 } | |
93 | 87 |
94 repositionImage(); | 88 /** |
89 * Shifts the cursor by the passed in values (in image coordinates). | |
90 * | |
91 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis. | |
92 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis. | |
93 * @return A point representing the new cursor position. | |
94 */ | |
95 public PointF moveCursorPosition(float deltaX, float deltaY) { | |
96 updateCursorPosition( | |
97 mCursorPosition.x + deltaX, mCursorPosition.y + deltaY, getImage Bounds()); | |
98 return new PointF(mCursorPosition.x, mCursorPosition.y); | |
95 } | 99 } |
96 | 100 |
97 /** | 101 /** |
98 * Shifts the viewport by the passed in values (in image coordinates). | 102 * Shifts the viewport by the passed in values (in image coordinates). |
99 * | 103 * |
100 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. | 104 * @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. | 105 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. |
102 */ | 106 */ |
103 public void moveViewportCenter(float deltaX, float deltaY) { | 107 public void moveViewportCenter(float deltaX, float deltaY) { |
104 // Offset and adjust the viewport center position to fit the screen. | 108 updateCursorPosition( |
105 mViewportPosition.offset(deltaX, deltaY); | 109 mCursorPosition.x + deltaX, mCursorPosition.y + deltaY, getViewp ortBounds()); |
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 } | |
114 | |
115 /** | |
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 } | 110 } |
126 | 111 |
127 /** | 112 /** |
128 * Handles System UI size and visibility changes. | 113 * Handles System UI size and visibility changes. |
129 * | 114 * |
130 * @param parameter The set of values defining the current System UI state. | 115 * @param parameter The set of values defining the current System UI state. |
131 */ | 116 */ |
132 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) { | 117 public void onSystemUiVisibilityChanged(SystemUiVisibilityChangedEventParame ter parameter) { |
133 if (parameter.softInputMethodVisible) { | 118 if (parameter.softInputMethodVisible) { |
134 mSystemUiScreenSize.set(parameter.left, parameter.top, | 119 mSystemUiScreenRect.set( |
135 mRenderData.screenWidth - parameter.right, | 120 parameter.left, parameter.top, parameter.right, parameter.bo ttom); |
136 mRenderData.screenHeight - parameter.bottom); | |
137 | 121 |
138 if (mAdjustViewportForSystemUi) { | 122 stopOffsetReductionAnimation(); |
139 // Adjust the cursor position to ensure it's visible when large System UI (1/3 or | 123 } else { |
140 // more of the total screen size) is displayed (typically the So ft Keyboard). | 124 mSystemUiScreenRect.setEmpty(); |
141 // Without this change, it is difficult for users to enter text into edit controls | 125 startOffsetReductionAnimation(); |
142 // which are located bottom of the screen and may not see the cu rsor at all. | 126 } |
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 | 127 |
150 // Apply the cursor offset. | 128 repositionImage(); |
151 setCursorPosition(mCursorPosition.x, mCursorPosition.y); | |
152 } | |
153 } else { | |
154 mCursorOffsetScreenY = 0.0f; | |
155 mSystemUiScreenSize.setEmpty(); | |
156 } | |
157 } | 129 } |
158 | 130 |
159 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { | 131 public void adjustViewportForSystemUi(boolean adjustViewportForSystemUi) { |
160 mAdjustViewportForSystemUi = adjustViewportForSystemUi; | 132 mAdjustViewportSizeForSystemUi = adjustViewportForSystemUi; |
133 | |
134 // The viewport center may have changed so reposition the image to refle ct the new value. | |
135 repositionImage(); | |
161 } | 136 } |
162 | 137 |
163 /** Resizes the image by zooming it such that the image is displayed without borders. */ | 138 /** Resizes the image by zooming it such that the image is displayed without borders. */ |
164 public void resizeImageToFitScreen() { | 139 public void resizeImageToFitScreen() { |
165 // Protect against being called before the image has been initialized. | 140 // Protect against being called before the image has been initialized. |
166 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { | 141 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { |
167 return; | 142 return; |
168 } | 143 } |
169 | 144 |
170 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; | 145 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; |
171 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; | 146 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; |
172 float screenToImageScale = Math.max(widthRatio, heightRatio); | 147 float screenToImageScale = Math.max(widthRatio, heightRatio); |
173 | 148 |
174 // If the image is smaller than the screen in either dimension, then we want to scale it | 149 // 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. | 150 // up to fit both and fill the screen with the image of the remote deskt op. |
176 if (screenToImageScale > 1.0f) { | 151 if (screenToImageScale > 1.0f) { |
177 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); | 152 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); |
178 } | 153 } |
179 } | 154 } |
180 | 155 |
181 /** | 156 /** |
182 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible | 157 * 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 | 158 * 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 | 159 * 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. | 160 * time, and to prevent arithmetic overflow problems from displaying the ima ge. |
186 * | 161 * |
187 * @param scaleFactor The factor used to zoom the canvas in or out. | 162 * @param scaleFactor The factor used to zoom the canvas in or out. |
188 * @param px The center x coordinate for the scale action. | 163 * @param px The center x coordinate for the scale action. |
189 * @param py The center y coordinate for the scale action. | 164 * @param py The center y coordinate for the scale action. |
190 * @param centerOnCursor Determines whether the viewport will be translated to the desired | 165 * @param centerOnCursor Determines whether the viewport will be translated to the desired |
191 * center position before being adjusted to fit the sc reen boundaries. | 166 * center position before being adjusted to fit the sc reen boundaries. |
192 */ | 167 */ |
193 public void scaleAndRepositionImage( | 168 public void scaleAndRepositionImage( |
(...skipping 16 matching lines...) Expand all Loading... | |
210 mRenderData.transform.mapVectors(imageSize); | 185 mRenderData.transform.mapVectors(imageSize); |
211 | 186 |
212 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { | 187 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { |
213 // Displayed image is too small in both directions, so apply the min imum zoom | 188 // Displayed image is too small in both directions, so apply the min imum zoom |
214 // level needed to fit either the width or height. | 189 // level needed to fit either the width or height. |
215 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, | 190 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, |
216 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); | 191 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); |
217 mRenderData.transform.setScale(scale, scale); | 192 mRenderData.transform.setScale(scale, scale); |
218 } | 193 } |
219 | 194 |
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) { | 195 if (centerOnCursor) { |
231 setCursorPosition(mCursorPosition.x, mCursorPosition.y); | 196 setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
232 } else { | 197 } else { |
233 // Find the new screen center (it was probably changed during the zo om operation) and | 198 // Find the new screen center (it probably changed during the zoom o peration) and update |
234 // update the viewport and cursor. | 199 // the viewport to smoothly track the zoom gesture. |
235 float[] mappedPoints = { | 200 float[] mappedPoints = {((float) mRenderData.screenWidth / 2) - mVie wportOffset.x, |
236 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)}; | 201 ((float) mRenderData.screenHeight / 2) - mViewportOffset.y}; |
237 Matrix screenToImage = new Matrix(); | 202 Matrix screenToImage = new Matrix(); |
238 mRenderData.transform.invert(screenToImage); | 203 mRenderData.transform.invert(screenToImage); |
239 screenToImage.mapPoints(mappedPoints); | 204 screenToImage.mapPoints(mappedPoints); |
240 // The cursor is mapped to the center of the viewport in this case. | 205 // The cursor is mapped to the center of the viewport in this case. |
241 setCursorPosition(mappedPoints[0], mappedPoints[1]); | 206 setViewportCenter(mappedPoints[0], mappedPoints[1]); |
242 } | 207 } |
243 } | 208 } |
244 | 209 |
245 /** | 210 /** |
211 * Sets the new cursor position, bounded by the given rect, and updates the image transform to | |
212 * reflect the new position. | |
213 */ | |
214 private void updateCursorPosition(float newX, float newY, RectF bounds) { | |
215 mCursorPosition.set(newX, newY); | |
216 constrainPointToBounds(mCursorPosition, bounds); | |
217 | |
218 calculateViewportOffset(newX - mCursorPosition.x, newY - mCursorPosition .y); | |
219 | |
220 repositionImage(); | |
221 } | |
222 | |
223 /** | |
224 * Returns the height of the screen (in screen coordinates) for use in calcu lations involving | |
225 * viewport positioning. | |
226 */ | |
227 private float getAdjustedScreenHeight() { | |
228 float adjustedScreenHeight; | |
229 if (mAdjustViewportSizeForSystemUi && !mSystemUiScreenRect.isEmpty()) { | |
230 // Find the center point of the viewport on the screen. | |
231 adjustedScreenHeight = mSystemUiScreenRect.bottom; | |
232 } else { | |
233 adjustedScreenHeight = ((float) mRenderData.screenHeight); | |
234 } | |
235 | |
236 return adjustedScreenHeight; | |
237 } | |
238 | |
239 /** | |
240 * Returns the center position of the viewport (in screen coordinates) inclu ding adjustments. | |
241 */ | |
242 private PointF getViewportCenter() { | |
243 return new PointF((float) mRenderData.screenWidth / 2, getAdjustedScreen Height() / 2); | |
244 } | |
245 | |
246 /** | |
246 * Repositions the image by translating it (without affecting the zoom level ). | 247 * Repositions the image by translating it (without affecting the zoom level ). |
247 */ | 248 */ |
248 private void repositionImage() { | 249 private void repositionImage() { |
249 // Map the current viewport position to screen coordinates and adjust th e image position. | 250 PointF viewportPosition = new PointF(mCursorPosition.x, mCursorPosition. y); |
250 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; | 251 constrainPointToBounds(viewportPosition, getViewportBounds()); |
251 mRenderData.transform.mapPoints(viewportPosition); | 252 float[] viewportAdjustment = {viewportPosition.x, viewportPosition.y}; |
253 mRenderData.transform.mapPoints(viewportAdjustment); | |
252 | 254 |
253 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; | 255 // Adjust the viewport to include the overpan amount. |
254 float viewportTransY = | 256 viewportAdjustment[0] += mViewportOffset.x; |
255 ((float) mRenderData.screenHeight / 2) - viewportPosition[1] - m CursorOffsetScreenY; | 257 viewportAdjustment[1] += mViewportOffset.y; |
256 | 258 |
257 // Translate the image to move the viewport to the expected screen locat ion. | 259 // Translate the image to move the viewport to the expected screen locat ion. |
258 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); | 260 PointF viewportCenter = getViewportCenter(); |
259 | 261 mRenderData.transform.postTranslate( |
260 updateVisibleImagePadding(); | 262 viewportCenter.x - viewportAdjustment[0], viewportCenter.y - vie wportAdjustment[1]); |
261 | 263 |
262 mRenderStub.setTransformation(mRenderData.transform); | 264 mRenderStub.setTransformation(mRenderData.transform); |
263 } | 265 } |
264 | 266 |
265 /** | 267 /** |
266 * Updates the given point such that it refers to a coordinate within the bo unds provided. | 268 * Updates the given point such that it refers to a coordinate within the bo unds provided. |
267 * | 269 * |
268 * @param point The point to adjust, the original object is modified. | 270 * @param point The point to adjust, the original object is modified. |
269 * @param bounds The bounds used to constrain the point. | 271 * @param bounds The bounds used to constrain the point. |
270 */ | 272 */ |
271 private void constrainPointToBounds(PointF point, RectF bounds) { | 273 private void constrainPointToBounds(PointF point, RectF bounds) { |
272 if (point.x < bounds.left) { | 274 if (point.x < bounds.left) { |
273 point.x = bounds.left; | 275 point.x = bounds.left; |
274 } else if (point.x > bounds.right) { | 276 } else if (point.x > bounds.right) { |
275 point.x = bounds.right; | 277 point.x = bounds.right; |
276 } | 278 } |
277 | 279 |
278 if (point.y < bounds.top) { | 280 if (point.y < bounds.top) { |
279 point.y = bounds.top; | 281 point.y = bounds.top; |
280 } else if (point.y > bounds.bottom) { | 282 } else if (point.y > bounds.bottom) { |
281 point.y = bounds.bottom; | 283 point.y = bounds.bottom; |
282 } | 284 } |
283 } | 285 } |
284 | 286 |
285 /** Returns a region which defines the set of valid cursor positions in imag e space. */ | 287 /** Returns a region which defines the set of valid cursor positions in imag e space. */ |
286 private RectF getImageBounds() { | 288 private RectF getImageBounds() { |
287 // The set of valid cursor positions includes any point on the image as well as the padding. | 289 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 } | 290 } |
309 | 291 |
310 /** Returns a region which defines the set of valid viewport center values i n image space. */ | 292 /** Returns a region which defines the set of valid viewport center values i n image space. */ |
311 private RectF getViewportBounds() { | 293 private RectF getViewportBounds() { |
312 // The region of allowable viewport values is the imageBound rect, inset by the size of the | 294 // 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 | 295 // 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. | 296 // allowing the user to see and interact with all pixels one the desktop image. |
315 Matrix screenToImage = new Matrix(); | 297 Matrix screenToImage = new Matrix(); |
316 mRenderData.transform.invert(screenToImage); | 298 mRenderData.transform.invert(screenToImage); |
317 | 299 |
318 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; | 300 PointF viewportCenter = getViewportCenter(); |
301 float[] screenVectors = {viewportCenter.x, viewportCenter.y}; | |
319 screenToImage.mapVectors(screenVectors); | 302 screenToImage.mapVectors(screenVectors); |
320 | 303 |
321 PointF letterboxPadding = getLetterboxPadding(); | 304 PointF letterboxPadding = getLetterboxPadding(); |
322 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; | 305 float[] letterboxPaddingVectors = {letterboxPadding.x, letterboxPadding. y}; |
323 screenToImage.mapVectors(letterboxPaddingVectors); | 306 screenToImage.mapVectors(letterboxPaddingVectors); |
324 | 307 |
325 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. | 308 // 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]; | 309 float screenCenterX = screenVectors[0] - letterboxPaddingVectors[0]; |
327 float screenCenterY = (screenVectors[1] / 2.0f) - letterboxPaddingVector s[1]; | 310 float screenCenterY = screenVectors[1] - letterboxPaddingVectors[1]; |
328 RectF imageBounds = getImageBounds(); | 311 RectF imageBounds = getImageBounds(); |
329 imageBounds.inset(screenCenterX, screenCenterY); | 312 imageBounds.inset(screenCenterX, screenCenterY); |
330 return imageBounds; | 313 return imageBounds; |
331 } | 314 } |
332 | 315 |
333 /** | 316 /** |
317 * Returns a region defining the maximum offset distance required to view th e entire image | |
318 * given the current amount of System UI overlapping it. | |
319 */ | |
320 private RectF getViewportOffsetBounds() { | |
321 // The allowable region is determined by: | |
322 // - Overlap of the System UI and image content | |
323 // - Current viewport offset | |
324 // | |
325 // The System UI overlap represents the maximum allowable offset, this i s used to bound the | |
326 // viewport movement in each direction. The current offset is used to p revent 'snapping' | |
327 // the image when the System UI overlap is reduced. | |
328 RectF viewportOffsetRect = new RectF(); | |
329 viewportOffsetRect.union(mViewportOffset.x, mViewportOffset.y); | |
330 | |
331 RectF systemUiOverlap = getSystemUiOverlap(); | |
332 return new RectF(Math.min(viewportOffsetRect.left, -systemUiOverlap.left ), | |
333 Math.min(viewportOffsetRect.top, -systemUiOverlap.top), | |
334 Math.max(viewportOffsetRect.right, systemUiOverlap.right), | |
335 Math.max(viewportOffsetRect.bottom, systemUiOverlap.bottom)); | |
336 } | |
337 | |
338 /** | |
334 * Provides the amount of padding needed to center the image content on the screen. | 339 * Provides the amount of padding needed to center the image content on the screen. |
335 */ | 340 */ |
336 private PointF getLetterboxPadding() { | 341 private PointF getLetterboxPadding() { |
337 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; | 342 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; |
338 mRenderData.transform.mapVectors(imageVectors); | 343 mRenderData.transform.mapVectors(imageVectors); |
339 | 344 |
340 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. | 345 // 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. | 346 // Since we center the image, split the difference so it is equally dist ributed. |
342 float widthAdjust = | 347 float widthAdjust = Math.max(((float) mRenderData.screenWidth - imageVec tors[0]) / 2, 0); |
343 Math.max(((float) mRenderData.screenWidth - imageVectors[0]) / 2 .0f, 0.0f); | 348 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 | 349 |
347 return new PointF(widthAdjust, heightAdjust); | 350 return new PointF(widthAdjust, heightAdjust); |
348 } | 351 } |
349 | 352 |
350 /** | 353 /** |
351 * Returns the amount of System UI along each edge of the screen which could overlap the remote | 354 * 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. | 355 * desktop image below it. This is the maximum amount that could overlap, n ot the actual value. |
353 */ | 356 */ |
354 private RectF getSystemUiOverlap() { | 357 private RectF getSystemUiOverlap() { |
355 // letterBox padding represents the space added to the image to center i t on the screen. | 358 if (mSystemUiScreenRect.isEmpty()) { |
359 return new RectF(); | |
360 } | |
361 | |
362 // 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 | 363 // 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. | 364 // between the System UI and the remote desktop image. |
358 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. | 365 // Note: Ignore negative padding (clamp to 0) since that means no overla p exists. |
366 float adjustedScreenHeight = getAdjustedScreenHeight(); | |
359 PointF letterboxPadding = getLetterboxPadding(); | 367 PointF letterboxPadding = getLetterboxPadding(); |
360 return new RectF(Math.max(mSystemUiScreenSize.left - letterboxPadding.x, 0.0f), | 368 return new RectF(Math.max(mSystemUiScreenRect.left - letterboxPadding.x, 0.0f), |
361 Math.max(mSystemUiScreenSize.top - letterboxPadding.y + mCursorO ffsetScreenY, 0.0f), | 369 Math.max(mSystemUiScreenRect.top - letterboxPadding.y, 0.0f), |
362 Math.max(mSystemUiScreenSize.right - letterboxPadding.x, 0.0f), | 370 Math.max(mRenderData.screenWidth - mSystemUiScreenRect.right - l etterboxPadding.x, |
363 Math.max(mSystemUiScreenSize.bottom - letterboxPadding.y - mCurs orOffsetScreenY, | 371 0.0f), |
372 Math.max(adjustedScreenHeight - mSystemUiScreenRect.bottom - let terboxPadding.y, | |
364 0.0f)); | 373 0.0f)); |
365 } | 374 } |
366 | 375 |
367 /** | 376 /** |
368 * Calculates the amount of padding visible on each edge of the desktop imag e. | 377 * Applies the given offset, as needed, to allow moving the image outside it s normal bounds. |
369 */ | 378 */ |
370 private void updateVisibleImagePadding() { | 379 private void calculateViewportOffset(float offsetX, float offsetY) { |
371 PointF letterboxPadding = getLetterboxPadding(); | 380 if (mSystemUiScreenRect.isEmpty()) { |
372 float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.i mageHeight}; | 381 // We only want to directly change the viewport offset when System U I is present. |
373 mRenderData.transform.mapPoints(imagePoints); | 382 return; |
383 } | |
374 | 384 |
375 mVisibleImagePadding.set(Math.max(imagePoints[0] - letterboxPadding.x, 0 .0f), | 385 float[] offsets = {offsetX, offsetY}; |
376 Math.max(imagePoints[1] - letterboxPadding.y, 0.0f), | 386 mRenderData.transform.mapVectors(offsets); |
377 Math.max(mRenderData.screenWidth - imagePoints[2] - letterboxPad ding.x, 0.0f), | 387 |
378 Math.max(mRenderData.screenHeight - imagePoints[3] - letterboxPa dding.y, 0.0f)); | 388 // Use a temporary variable here as {@link #getViewportOffsetBounds()} u ses the current |
389 // viewport offset as a maximum boundary. If we add the offset first, t he result ends up | |
390 // being unbounded. Thus we use a temporary object for the boundary cal culation. | |
391 PointF requestedOffset = | |
392 new PointF(mViewportOffset.x + offsets[0], mViewportOffset.y + o ffsets[1]); | |
393 constrainPointToBounds(requestedOffset, getViewportOffsetBounds()); | |
394 mViewportOffset.set(requestedOffset); | |
395 } | |
396 | |
397 /** | |
398 * Starts an animation to smoothly reduce the viewport offset. Does nothing if an animation is | |
399 * already running or the offset is already 0. | |
400 */ | |
401 private void startOffsetReductionAnimation() { | |
402 if (mFrameRenderedCallback != null || Math.abs(mViewportOffset.length()) < EPSILON) { | |
403 return; | |
404 } | |
405 | |
406 mFrameRenderedCallback = new Event.ParameterCallback<Boolean, Void>() { | |
407 @Override | |
408 public Boolean run(Void p) { | |
409 mViewportOffset.set(mViewportOffset.x * VIEWPORT_OFFSET_REDUCTIO N_FACTOR, | |
Yuwei
2016/10/25 08:25:26
The timing implicitly depends on the fact that the
joedow
2016/10/25 16:49:50
I didn't realize the renderer wasn't running at a
Yuwei
2016/10/25 18:19:59
60Hz is more like the highest possible framerate o
| |
410 mViewportOffset.y * VIEWPORT_OFFSET_REDUCTION_FACTOR); | |
411 | |
412 if (Math.abs(mViewportOffset.length()) < MINIMUM_VIEWPORT_OFFSET _PIXELS) { | |
413 mViewportOffset.set(0.0f, 0.0f); | |
414 mFrameRenderedCallback = null; | |
415 } | |
416 | |
417 repositionImage(); | |
418 | |
419 return mFrameRenderedCallback != null; | |
420 } | |
421 }; | |
422 | |
423 mRenderStub.onCanvasRendered().addSelfRemovable(mFrameRenderedCallback); | |
424 } | |
425 | |
426 /** | |
427 * Stops an existing animation if one is currently running. | |
428 */ | |
429 private void stopOffsetReductionAnimation() { | |
430 if (mFrameRenderedCallback == null) { | |
431 return; | |
432 } | |
433 | |
434 mRenderStub.onCanvasRendered().remove(mFrameRenderedCallback); | |
Yuwei
2016/10/25 08:25:26
This line of code probably doesn't work. addSelfRe
joedow
2016/10/25 16:49:50
Thanks for pointing that out, I can see the intent
Yuwei
2016/10/25 18:19:59
Agreed. We may want to fix the self removable at s
| |
435 mFrameRenderedCallback = null; | |
379 } | 436 } |
380 } | 437 } |
OLD | NEW |