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 /** |
17 * Maximum allowed zoom level - see {@link #repositionImageWithZoom()}. | 17 * Maximum allowed zoom level - see {@link #repositionImageWithZoom()}. |
18 */ | 18 */ |
19 private static final float MAX_ZOOM_FACTOR = 100.0f; | 19 private static final float MAX_ZOOM_FACTOR = 100.0f; |
20 | 20 |
21 private final RenderStub mRenderStub; | 21 private final RenderStub mRenderStub; |
22 private final RenderData mRenderData; | 22 private final RenderData mRenderData; |
23 | 23 |
24 /** | 24 /** |
25 * Represents the actual center of the viewport. This value needs to be a p air of floats so the | |
26 * desktop image can be positioned with sub-pixel accuracy for smoother pann ing animations at | |
27 * high zoom levels. | |
28 */ | |
29 private PointF mViewportPosition = new PointF(); | |
30 | |
31 /** | |
25 * Represents the desired center of the viewport. This value may not repres ent the actual | 32 * Represents the desired center of the viewport. This value may not repres ent the actual |
26 * center of the viewport as adjustments are made to ensure as much of the d esktop is visible as | 33 * center of the viewport as adjustments are made to ensure as much of the d esktop is visible as |
27 * possible. This value needs to be a pair of floats so the desktop image c an be positioned | 34 * possible. This value needs to be a pair of floats so the desktop image c an be positioned |
28 * with sub-pixel accuracy for smoother panning animations at high zoom leve ls. | 35 * with sub-pixel accuracy for smoother panning animations at high zoom leve ls. |
29 */ | 36 */ |
30 private PointF mViewportPosition = new PointF(); | 37 private PointF mCursorPosition = new PointF(); |
31 | 38 |
32 /** | 39 /** |
33 * Represents the amount of space, in pixels, used by System UI. | 40 * Represents the amount of space, in pixels, used by System UI. |
34 */ | 41 */ |
35 private Rect mSystemUiOffsetPixels = new Rect(); | 42 private Rect mSystemUiOffsetPixels = new Rect(); |
36 | 43 |
37 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { | 44 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
38 mRenderStub = renderStub; | 45 mRenderStub = renderStub; |
39 mRenderData = renderData; | 46 mRenderData = renderData; |
40 } | 47 } |
41 | 48 |
42 /** | 49 /** |
43 * Shifts the viewport by the passed in deltas (in image coordinates). | |
44 * | |
45 * @param useScreenCenter Determines whether to use the desired viewport pos ition or the actual | |
46 * center of the screen for positioning. | |
47 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. | |
48 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. | |
49 * @return A point representing the new center position of the viewport. | |
50 */ | |
51 public PointF moveViewportCenter(boolean useScreenCenter, float deltaX, floa t deltaY) { | |
52 PointF viewportCenter; | |
53 if (useScreenCenter) { | |
54 viewportCenter = getTrueViewportCenter(); | |
55 } else { | |
56 viewportCenter = new PointF(mViewportPosition.x, mViewportPosition.y ); | |
57 } | |
58 viewportCenter.offset(deltaX, deltaY); | |
59 | |
60 RectF bounds = new RectF(0, 0, mRenderData.imageWidth, mRenderData.image Height); | |
61 | |
62 if (viewportCenter.x < bounds.left) { | |
63 viewportCenter.x = bounds.left; | |
64 } else if (viewportCenter.x > bounds.right) { | |
65 viewportCenter.x = bounds.right; | |
66 } | |
67 | |
68 if (viewportCenter.y < bounds.top) { | |
69 viewportCenter.y = bounds.top; | |
70 } else if (viewportCenter.y > bounds.bottom) { | |
71 viewportCenter.y = bounds.bottom; | |
72 } | |
73 | |
74 mViewportPosition.set(viewportCenter); | |
75 | |
76 return viewportCenter; | |
77 } | |
78 | |
79 /** | |
80 * Sets the desired center position of the viewport. | 50 * Sets the desired center position of the viewport. |
81 * | 51 * |
82 * @param newX The new x coordinate value for the desired center position. | 52 * @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. | 53 * @param newY The new y coordinate value for the desired center position. |
84 */ | 54 */ |
85 public void setViewportPosition(float newX, float newY) { | 55 public void setViewportCenter(float newX, float newY) { |
Lambros
2016/09/27 02:16:37
This should probably be called setCursorAndViewpor
joedow
2016/09/27 20:09:29
Updating the name is fine, but I'd prefer to name
| |
86 mViewportPosition.set(newX, newY); | 56 // First set the cursor position since its potential values are a supers et of the viewport. |
57 mCursorPosition.set(newX, newY); | |
58 constrainPointToBounds(mCursorPosition, getImageBounds()); | |
59 | |
60 // Now set the viewport position based on the cursor. | |
61 mViewportPosition.set(mCursorPosition); | |
62 constrainPointToBounds(mViewportPosition, getViewportBounds()); | |
87 } | 63 } |
88 | 64 |
89 /** | 65 /** |
90 * Returns the true center position of the viewport (in image coordinates). | 66 * Shifts the viewport by the passed in values (in image coordinates). |
91 * | 67 * |
92 * @return A point representing the true center position of the viewport. | 68 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis. |
69 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis. | |
93 */ | 70 */ |
94 private PointF getTrueViewportCenter() { | 71 public void moveViewportCenter(float deltaX, float deltaY) { |
Lambros
2016/09/27 02:16:37
Clarify this does not actually move the viewport -
joedow
2016/09/27 20:09:29
Acknowledged.
| |
95 // Find the center point of the viewport on the screen. | 72 // Offset and adjust the viewport center position to fit the screen. |
96 float[] viewportPosition = { | 73 mViewportPosition.offset(deltaX, deltaY); |
97 ((float) mRenderData.screenWidth / 2), ((float) mRenderData.scre enHeight / 2)}; | 74 constrainPointToBounds(mViewportPosition, getViewportBounds()); |
98 | 75 |
99 // Convert the screen position to an image position. | 76 // We don't need to constrain the cursor position as the viewport positi on is always within |
100 Matrix screenToImage = new Matrix(); | 77 // the bounds of the potential cursor positions. |
101 mRenderData.transform.invert(screenToImage); | 78 mCursorPosition.set(mViewportPosition); |
102 screenToImage.mapPoints(viewportPosition); | |
103 return new PointF(viewportPosition[0], viewportPosition[1]); | |
104 } | 79 } |
105 | 80 |
106 /** | 81 /** |
82 * Shifts the cursor by the passed in values (in image coordinates) and adju sts the viewport. | |
83 * | |
84 * @param deltaX The distance (in image coordinates) to move the cursor alon g the x-axis. | |
85 * @param deltaY The distance (in image coordinates) to move the cursor alon g the y-axis. | |
86 * @return A point representing the new cursor position. | |
87 */ | |
88 public PointF moveViewportWithCursor(float deltaX, float deltaY) { | |
Lambros
2016/09/27 02:16:36
Maybe offsetCursorAndSetViewportPosition() ?
joedow
2016/09/27 20:09:29
Renamed based on intention of the caller.
| |
89 setViewportCenter(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY ); | |
90 return new PointF(mCursorPosition.x, mCursorPosition.y); | |
91 } | |
92 | |
93 /** | |
107 * Sets the offset values used to calculate the space used by System UI. | 94 * Sets the offset values used to calculate the space used by System UI. |
108 * | 95 * |
109 * @param left The space used by System UI on the left edge of the screen. | 96 * @param left The space used by System UI on the left edge of the screen. |
110 * @param top The space used by System UI on the top edge of the screen. | 97 * @param top The space used by System UI on the top edge of the screen. |
111 * @param right The space used by System UI on the right edge of the screen. | 98 * @param right The space used by System UI on the right edge of the screen. |
112 * @param bottom The space used by System UI on the bottom edge of the scree n. | 99 * @param bottom The space used by System UI on the bottom edge of the scree n. |
113 */ | 100 */ |
114 public void setSystemUiOffsetValues(int left, int top, int right, int bottom ) { | 101 public void setSystemUiOffsetValues(int left, int top, int right, int bottom ) { |
115 mSystemUiOffsetPixels.set(left, top, right, bottom); | 102 mSystemUiOffsetPixels.set(left, top, right, bottom); |
116 } | 103 } |
(...skipping 12 matching lines...) Expand all Loading... | |
129 | 116 |
130 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; | 117 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; |
131 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; | 118 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; |
132 float screenToImageScale = Math.max(widthRatio, heightRatio); | 119 float screenToImageScale = Math.max(widthRatio, heightRatio); |
133 | 120 |
134 // If the image is smaller than the screen in either dimension, then we want to scale it | 121 // If the image is smaller than the screen in either dimension, then we want to scale it |
135 // up to fit both and fill the screen with the image of the remote deskt op. | 122 // up to fit both and fill the screen with the image of the remote deskt op. |
136 if (screenToImageScale > 1.0f) { | 123 if (screenToImageScale > 1.0f) { |
137 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); | 124 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); |
138 } | 125 } |
139 | |
140 repositionImage(false); | |
141 } | 126 } |
142 | 127 |
143 /** | 128 /** |
144 * Repositions the image by translating it (without affecting the zoom level ). | 129 * Repositions the image by translating it (without affecting the zoom level ). |
145 * | |
146 * @param centerViewport Determines whether the viewport will be translated to the desired | |
147 * center position before being adjusted to fit the sc reen boundaries. | |
148 */ | 130 */ |
149 public void repositionImage(boolean centerViewport) { | 131 public void repositionImage() { |
150 // The goal of the code below is to position the viewport as close to th e desired center | 132 // The goal of the code below is to position the viewport as close to th e desired center |
Lambros
2016/09/27 02:16:36
The phrase 'desired center' is how you've describe
joedow
2016/09/27 20:09:29
Acknowledged.
| |
151 // position as possible whilst keeping as much of the desktop in view as possible. | 133 // position as possible whilst keeping as much of the desktop in view as possible. |
152 // To achieve these goals, we first position the desktop image at the de sired center | 134 // To achieve these goals, we first position the desktop image at the de sired center |
153 // point and then re-position it to maximize the viewable area. | 135 // point and then re-position it to maximize the viewable area. |
154 if (centerViewport) { | 136 moveViewportToPoint(mViewportPosition); |
Lambros
2016/09/27 02:16:37
This call modifies the transformation matrix so th
joedow
2016/09/27 20:09:28
Acknowledged.
| |
155 // Map the current viewport position to screen coordinates. | 137 translateImageToFitScreen(); |
Lambros
2016/09/27 02:16:37
But this call will modify the matrix *without* upd
joedow
2016/09/27 20:09:29
Acknowledged.
| |
156 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y }; | 138 } |
157 mRenderData.transform.mapPoints(viewportPosition); | |
158 | 139 |
159 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewp ortPosition[0]; | 140 /** |
160 float viewportTransY = ((float) mRenderData.screenHeight / 2) - view portPosition[1]; | 141 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible |
161 | 142 * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The |
162 // Translate so the viewport is displayed in the middle of the scree n. | 143 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable |
163 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); | 144 * time, and to prevent arithmetic overflow problems from displaying the ima ge. |
145 * | |
146 * @param centerOnCursor Determines whether the viewport will be translated to the desired | |
147 * center position before being adjusted to fit the sc reen boundaries. | |
148 */ | |
149 public void repositionImageWithZoom(boolean centerOnCursor) { | |
150 // Avoid division by zero in case this gets called before the image size is initialized. | |
151 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { | |
152 return; | |
164 } | 153 } |
165 | 154 |
155 // Zoom out if the zoom level is too high. | |
156 float currentZoomLevel = mRenderData.transform.mapRadius(1.0f); | |
157 if (currentZoomLevel > MAX_ZOOM_FACTOR) { | |
158 mRenderData.transform.setScale(MAX_ZOOM_FACTOR, MAX_ZOOM_FACTOR); | |
159 } | |
160 | |
161 // Get image size scaled to screen coordinates. | |
162 float[] imageSize = {mRenderData.imageWidth, mRenderData.imageHeight}; | |
163 mRenderData.transform.mapVectors(imageSize); | |
164 | |
165 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { | |
166 // Displayed image is too small in both directions, so apply the min imum zoom | |
167 // level needed to fit either the width or height. | |
168 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, | |
169 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); | |
170 mRenderData.transform.setScale(scale, scale); | |
171 } | |
172 | |
173 if (centerOnCursor) { | |
174 moveViewportToPoint(mCursorPosition); | |
175 } | |
176 | |
177 translateImageToFitScreen(); | |
178 | |
179 // Update our position members to reflect the new position of the image. | |
180 PointF newPosition; | |
181 if (centerOnCursor) { | |
182 newPosition = new PointF(mCursorPosition.x, mCursorPosition.y); | |
183 } else { | |
184 float screenCenterX = (float) mRenderData.screenWidth / 2; | |
185 float screenCenterY = (float) mRenderData.screenHeight / 2; | |
186 float[] mappedPoints = {screenCenterX, screenCenterY}; | |
187 Matrix screenToImage = new Matrix(); | |
188 mRenderData.transform.invert(screenToImage); | |
189 screenToImage.mapPoints(mappedPoints); | |
190 newPosition = new PointF(mappedPoints[0], mappedPoints[1]); | |
191 } | |
192 setViewportCenter(newPosition.x, newPosition.y); | |
193 } | |
194 | |
195 /** | |
196 * Updates the given point such that it refers to a coordinate within the bo unds provided. | |
197 * | |
198 * @param point The point to adjust, the original object is modified. | |
199 * @param bounds The bounds used to constrain the point. | |
200 */ | |
201 private void constrainPointToBounds(PointF point, RectF bounds) { | |
202 if (point.x < bounds.left) { | |
203 point.x = bounds.left; | |
204 } else if (point.x > bounds.right) { | |
205 point.x = bounds.right; | |
206 } | |
207 | |
208 if (point.y < bounds.top) { | |
209 point.y = bounds.top; | |
210 } else if (point.y > bounds.bottom) { | |
211 point.y = bounds.bottom; | |
212 } | |
213 } | |
214 | |
215 /** Returns a region which defines the set of valid cursor values in image s pace. */ | |
216 private RectF getImageBounds() { | |
217 return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); | |
218 } | |
219 | |
220 /** Returns a region which defines the set of valid viewport center values i n image space. */ | |
221 private RectF getViewportBounds() { | |
Lambros
2016/09/27 02:16:37
IIUC, this is precisely the set of points for whic
joedow
2016/09/27 20:09:29
I was moving to a place where multiple transforms
| |
222 float[] screenVectors = { | |
223 ((float) mRenderData.screenWidth / 2), ((float) mRenderData.scre enHeight / 2)}; | |
224 Matrix screenToImage = new Matrix(); | |
225 mRenderData.transform.invert(screenToImage); | |
226 screenToImage.mapVectors(screenVectors); | |
227 return new RectF(screenVectors[0], screenVectors[1], | |
228 mRenderData.imageWidth - screenVectors[0], | |
229 mRenderData.imageHeight - screenVectors[1]); | |
230 } | |
231 | |
232 /** Centers the viewport on the passed in point. */ | |
233 private void moveViewportToPoint(PointF point) { | |
234 // Map the current viewport position to screen coordinates. | |
235 float[] viewportPosition = {point.x, point.y}; | |
236 mRenderData.transform.mapPoints(viewportPosition); | |
237 | |
238 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; | |
239 float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewport Position[1]; | |
240 | |
241 // Translate so the viewport is displayed in the middle of the screen. | |
242 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); | |
243 } | |
244 | |
245 /** Repositions the image by translating it to maximize the amount of viewab le content. */ | |
246 private void translateImageToFitScreen() { | |
166 // Get the coordinates of the desktop rectangle (top-left/bottom-right c orners) in | 247 // Get the coordinates of the desktop rectangle (top-left/bottom-right c orners) in |
167 // screen coordinates. Order is: left, top, right, bottom. | 248 // screen coordinates. Order is: left, top, right, bottom. |
168 RectF rectScreen = new RectF(0, 0, mRenderData.imageWidth, mRenderData.i mageHeight); | 249 RectF rectScreen = new RectF(0, 0, mRenderData.imageWidth, mRenderData.i mageHeight); |
169 mRenderData.transform.mapRect(rectScreen); | 250 mRenderData.transform.mapRect(rectScreen); |
170 | 251 |
171 float leftDelta = rectScreen.left; | 252 float leftDelta = rectScreen.left; |
172 float rightDelta = | 253 float rightDelta = rectScreen.right - mRenderData.screenWidth; |
173 rectScreen.right - mRenderData.screenWidth + mSystemUiOffsetPixe ls.right; | |
174 float topDelta = rectScreen.top; | 254 float topDelta = rectScreen.top; |
175 float bottomDelta = | 255 float bottomDelta = rectScreen.bottom - mRenderData.screenHeight; |
176 rectScreen.bottom - mRenderData.screenHeight + mSystemUiOffsetPi xels.bottom; | |
177 float xAdjust = 0; | 256 float xAdjust = 0; |
178 float yAdjust = 0; | 257 float yAdjust = 0; |
179 | 258 |
180 if (rectScreen.right - rectScreen.left < mRenderData.screenWidth) { | 259 if (rectScreen.right - rectScreen.left < mRenderData.screenWidth) { |
181 // Image is narrower than the screen, so center it. | 260 // Image is narrower than the screen, so center it. |
182 xAdjust = -(rightDelta + leftDelta) / 2; | 261 xAdjust = -(rightDelta + leftDelta) / 2; |
183 } else if (leftDelta > 0 && rightDelta > 0) { | 262 } else if (leftDelta > 0 && rightDelta > 0) { |
184 // Panning the image left will show more of it. | 263 // Panning the image left will show more of it. |
185 xAdjust = -Math.min(leftDelta, rightDelta); | 264 xAdjust = -Math.min(leftDelta, rightDelta); |
186 } else if (leftDelta < 0 && rightDelta < 0) { | 265 } else if (leftDelta < 0 && rightDelta < 0) { |
187 // Pan the image right. | 266 // Pan the image right. |
188 xAdjust = Math.min(-leftDelta, -rightDelta); | 267 xAdjust = Math.min(-leftDelta, -rightDelta); |
189 } | 268 } |
190 | 269 |
191 // Apply similar logic for yAdjust. | 270 // Apply similar logic for yAdjust. |
192 if (rectScreen.bottom - rectScreen.top < mRenderData.screenHeight) { | 271 if (rectScreen.bottom - rectScreen.top < mRenderData.screenHeight) { |
193 yAdjust = -(bottomDelta + topDelta) / 2; | 272 yAdjust = -(bottomDelta + topDelta) / 2; |
194 } else if (topDelta > 0 && bottomDelta > 0) { | 273 } else if (topDelta > 0 && bottomDelta > 0) { |
195 yAdjust = -Math.min(topDelta, bottomDelta); | 274 yAdjust = -Math.min(topDelta, bottomDelta); |
196 } else if (topDelta < 0 && bottomDelta < 0) { | 275 } else if (topDelta < 0 && bottomDelta < 0) { |
197 yAdjust = Math.min(-topDelta, -bottomDelta); | 276 yAdjust = Math.min(-topDelta, -bottomDelta); |
198 } | 277 } |
199 | 278 |
200 mRenderData.transform.postTranslate(xAdjust, yAdjust); | 279 mRenderData.transform.postTranslate(xAdjust, yAdjust); |
Lambros
2016/09/27 02:16:37
By actually transforming the matrix here, you brea
joedow
2016/09/27 20:09:29
Acknowledged.
| |
201 | 280 |
202 mRenderStub.setTransformation(mRenderData.transform); | 281 mRenderStub.setTransformation(mRenderData.transform); |
203 } | 282 } |
204 | |
205 /** | |
206 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible | |
207 * limits. The minimum zoom level is chosen to avoid black space around all 4 sides. The | |
208 * maximum zoom level is set arbitrarily, so that the user can zoom out agai n in a reasonable | |
209 * time, and to prevent arithmetic overflow problems from displaying the ima ge. | |
210 * | |
211 * @param centerViewport Determines whether the viewport will be translated to the desired | |
212 * center position before being adjusted to fit the sc reen boundaries. | |
213 */ | |
214 public void repositionImageWithZoom(boolean centerViewport) { | |
215 // Avoid division by zero in case this gets called before the image size is initialized. | |
216 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { | |
217 return; | |
218 } | |
219 | |
220 // Zoom out if the zoom level is too high. | |
221 float currentZoomLevel = mRenderData.transform.mapRadius(1.0f); | |
222 if (currentZoomLevel > MAX_ZOOM_FACTOR) { | |
223 mRenderData.transform.setScale(MAX_ZOOM_FACTOR, MAX_ZOOM_FACTOR); | |
224 } | |
225 | |
226 // Get image size scaled to screen coordinates. | |
227 float[] imageSize = {mRenderData.imageWidth, mRenderData.imageHeight}; | |
228 mRenderData.transform.mapVectors(imageSize); | |
229 | |
230 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { | |
231 // Displayed image is too small in both directions, so apply the min imum zoom | |
232 // level needed to fit either the width or height. | |
233 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, | |
234 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); | |
235 mRenderData.transform.setScale(scale, scale); | |
236 } | |
237 | |
238 repositionImage(centerViewport); | |
239 } | |
240 } | 283 } |
OLD | NEW |