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