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

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

Issue 2372663002: Separating cursor and viewport calculations in the desktop canvas (Closed)
Patch Set: Updating viewport bound calcs and addressing other feedback Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | remoting/android/java/src/org/chromium/chromoting/TouchInputHandler.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chromoting; 5 package org.chromium.chromoting;
6 6
7 import android.graphics.Matrix; 7 import android.graphics.Matrix;
8 import android.graphics.PointF; 8 import android.graphics.PointF;
9 import android.graphics.Rect; 9 import android.graphics.Rect;
10 import android.graphics.RectF; 10 import android.graphics.RectF;
11 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). 50 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures
44 * 51 * the viewport is updated to include the cursor within it.
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.
81 * 52 *
82 * @param newX The new x coordinate value for the desired center position. 53 * @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. 54 * @param newY The new y coordinate value for the desired center position.
84 */ 55 */
85 public void setViewportPosition(float newX, float newY) { 56 public void setCursorPosition(float newX, float newY) {
86 mViewportPosition.set(newX, newY); 57 // First set the cursor position since its potential values are a supers et of the viewport.
58 mCursorPosition.set(newX, newY);
59 constrainPointToBounds(mCursorPosition, getImageBounds());
60
61 // Now set the viewport position based on the cursor.
62 mViewportPosition.set(mCursorPosition);
63 constrainPointToBounds(mViewportPosition, getViewportBounds());
64
65 repositionImage();
87 } 66 }
88 67
89 /** 68 /**
90 * Returns the true center position of the viewport (in image coordinates). 69 * Shifts the viewport by the passed in values (in image coordinates).
91 * 70 *
92 * @return A point representing the true center position of the viewport. 71 * @param deltaX The distance (in image coordinates) to move the viewport al ong the x-axis.
72 * @param deltaY The distance (in image coordinates) to move the viewport al ong the y-axis.
93 */ 73 */
94 private PointF getTrueViewportCenter() { 74 public void moveViewportCenter(float deltaX, float deltaY) {
95 // Find the center point of the viewport on the screen. 75 // Offset and adjust the viewport center position to fit the screen.
96 float[] viewportPosition = { 76 mViewportPosition.offset(deltaX, deltaY);
97 ((float) mRenderData.screenWidth / 2), ((float) mRenderData.scre enHeight / 2)}; 77 constrainPointToBounds(mViewportPosition, getViewportBounds());
98 78
99 // Convert the screen position to an image position. 79 // We don't need to constrain the cursor position as the viewport positi on is always within
100 Matrix screenToImage = new Matrix(); 80 // the bounds of the potential cursor positions.
101 mRenderData.transform.invert(screenToImage); 81 mCursorPosition.set(mViewportPosition);
Yuwei 2016/09/27 22:01:55 Just curious that why do we need to move the curso
joedow 2016/09/27 22:23:37 I wanted to keep the Cursor and Viewport in sync t
Yuwei 2016/09/27 22:28:10 Acknowledged.
102 screenToImage.mapPoints(viewportPosition); 82
103 return new PointF(viewportPosition[0], viewportPosition[1]); 83 repositionImage();
104 } 84 }
105 85
106 /** 86 /**
87 * Shifts the cursor by the passed in values (in image coordinates) and adju sts the viewport.
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 setCursorPosition(mCursorPosition.x + deltaX, mCursorPosition.y + deltaY );
95 return new PointF(mCursorPosition.x, mCursorPosition.y);
96 }
97
98 /**
107 * Sets the offset values used to calculate the space used by System UI. 99 * Sets the offset values used to calculate the space used by System UI.
108 * 100 *
109 * @param left The space used by System UI on the left edge of the screen. 101 * @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. 102 * @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. 103 * @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. 104 * @param bottom The space used by System UI on the bottom edge of the scree n.
113 */ 105 */
114 public void setSystemUiOffsetValues(int left, int top, int right, int bottom ) { 106 public void setSystemUiOffsetValues(int left, int top, int right, int bottom ) {
115 mSystemUiOffsetPixels.set(left, top, right, bottom); 107 mSystemUiOffsetPixels.set(left, top, right, bottom);
116 } 108 }
117 109
118 /** Called to indicate that no System UI is visible. */ 110 /** Called to indicate that no System UI is visible. */
119 public void clearSystemUiOffsets() { 111 public void clearSystemUiOffsets() {
120 mSystemUiOffsetPixels.setEmpty(); 112 mSystemUiOffsetPixels.setEmpty();
121 } 113 }
122 114
123 /** Repositions the image by zooming it such that the image is displayed wit hout borders. */ 115 /** Resizes the image by zooming it such that the image is displayed without borders. */
124 public void resizeImageToFitScreen() { 116 public void resizeImageToFitScreen() {
125 // Protect against being called before the image has been initialized. 117 // Protect against being called before the image has been initialized.
126 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { 118 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) {
127 return; 119 return;
128 } 120 }
129 121
130 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth; 122 float widthRatio = (float) mRenderData.screenWidth / mRenderData.imageWi dth;
131 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height; 123 float heightRatio = (float) mRenderData.screenHeight / mRenderData.image Height;
132 float screenToImageScale = Math.max(widthRatio, heightRatio); 124 float screenToImageScale = Math.max(widthRatio, heightRatio);
133 125
134 // If the image is smaller than the screen in either dimension, then we want to scale it 126 // 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. 127 // up to fit both and fill the screen with the image of the remote deskt op.
136 if (screenToImageScale > 1.0f) { 128 if (screenToImageScale > 1.0f) {
137 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e); 129 mRenderData.transform.setScale(screenToImageScale, screenToImageScal e);
138 } 130 }
139
140 repositionImage(false);
141 } 131 }
142 132
143 /** 133 /**
144 * 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 */
149 public void repositionImage(boolean centerViewport) {
150 // The goal of the code below is to position the viewport as close to th e desired center
151 // 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
153 // point and then re-position it to maximize the viewable area.
154 if (centerViewport) {
155 // Map the current viewport position to screen coordinates.
156 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y };
157 mRenderData.transform.mapPoints(viewportPosition);
158
159 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewp ortPosition[0];
160 float viewportTransY = ((float) mRenderData.screenHeight / 2) - view portPosition[1];
161
162 // Translate so the viewport is displayed in the middle of the scree n.
163 mRenderData.transform.postTranslate(viewportTransX, viewportTransY);
164 }
165
166 // Get the coordinates of the desktop rectangle (top-left/bottom-right c orners) in
167 // screen coordinates. Order is: left, top, right, bottom.
168 RectF rectScreen = new RectF(0, 0, mRenderData.imageWidth, mRenderData.i mageHeight);
169 mRenderData.transform.mapRect(rectScreen);
170
171 float leftDelta = rectScreen.left;
172 float rightDelta =
173 rectScreen.right - mRenderData.screenWidth + mSystemUiOffsetPixe ls.right;
174 float topDelta = rectScreen.top;
175 float bottomDelta =
176 rectScreen.bottom - mRenderData.screenHeight + mSystemUiOffsetPi xels.bottom;
177 float xAdjust = 0;
178 float yAdjust = 0;
179
180 if (rectScreen.right - rectScreen.left < mRenderData.screenWidth) {
181 // Image is narrower than the screen, so center it.
182 xAdjust = -(rightDelta + leftDelta) / 2;
183 } else if (leftDelta > 0 && rightDelta > 0) {
184 // Panning the image left will show more of it.
185 xAdjust = -Math.min(leftDelta, rightDelta);
186 } else if (leftDelta < 0 && rightDelta < 0) {
187 // Pan the image right.
188 xAdjust = Math.min(-leftDelta, -rightDelta);
189 }
190
191 // Apply similar logic for yAdjust.
192 if (rectScreen.bottom - rectScreen.top < mRenderData.screenHeight) {
193 yAdjust = -(bottomDelta + topDelta) / 2;
194 } else if (topDelta > 0 && bottomDelta > 0) {
195 yAdjust = -Math.min(topDelta, bottomDelta);
196 } else if (topDelta < 0 && bottomDelta < 0) {
197 yAdjust = Math.min(-topDelta, -bottomDelta);
198 }
199
200 mRenderData.transform.postTranslate(xAdjust, yAdjust);
201
202 mRenderStub.setTransformation(mRenderData.transform);
203 }
204
205 /**
206 * Repositions the image by translating and zooming it, to keep the zoom lev el within sensible 134 * 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 135 * 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 136 * 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. 137 * time, and to prevent arithmetic overflow problems from displaying the ima ge.
210 * 138 *
211 * @param centerViewport Determines whether the viewport will be translated to the desired 139 * @param scaleFactor The factor used to zoom the canvas in or out.
140 * @param px The center x coordinate for the scale action.
141 * @param py The center y coordinate for the scale action.
142 * @param centerOnCursor Determines whether the viewport will be translated to the desired
212 * center position before being adjusted to fit the sc reen boundaries. 143 * center position before being adjusted to fit the sc reen boundaries.
213 */ 144 */
214 public void repositionImageWithZoom(boolean centerViewport) { 145 public void scaleAndRepositionImage(
146 float scaleFactor, float px, float py, boolean centerOnCursor) {
215 // Avoid division by zero in case this gets called before the image size is initialized. 147 // Avoid division by zero in case this gets called before the image size is initialized.
216 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) { 148 if (mRenderData.imageWidth == 0 || mRenderData.imageHeight == 0) {
217 return; 149 return;
218 } 150 }
219 151
152 mRenderData.transform.postScale(scaleFactor, scaleFactor, px, py);
153
220 // Zoom out if the zoom level is too high. 154 // Zoom out if the zoom level is too high.
221 float currentZoomLevel = mRenderData.transform.mapRadius(1.0f); 155 float currentZoomLevel = mRenderData.transform.mapRadius(1.0f);
222 if (currentZoomLevel > MAX_ZOOM_FACTOR) { 156 if (currentZoomLevel > MAX_ZOOM_FACTOR) {
223 mRenderData.transform.setScale(MAX_ZOOM_FACTOR, MAX_ZOOM_FACTOR); 157 mRenderData.transform.setScale(MAX_ZOOM_FACTOR, MAX_ZOOM_FACTOR);
224 } 158 }
225 159
226 // Get image size scaled to screen coordinates. 160 // Get image size scaled to screen coordinates.
227 float[] imageSize = {mRenderData.imageWidth, mRenderData.imageHeight}; 161 float[] imageSize = {mRenderData.imageWidth, mRenderData.imageHeight};
228 mRenderData.transform.mapVectors(imageSize); 162 mRenderData.transform.mapVectors(imageSize);
229 163
230 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { 164 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) {
231 // Displayed image is too small in both directions, so apply the min imum zoom 165 // Displayed image is too small in both directions, so apply the min imum zoom
232 // level needed to fit either the width or height. 166 // level needed to fit either the width or height.
233 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, 167 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth,
234 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); 168 (float) mRenderData.screenHeight / mRenderDat a.imageHeight);
235 mRenderData.transform.setScale(scale, scale); 169 mRenderData.transform.setScale(scale, scale);
236 } 170 }
237 171
238 repositionImage(centerViewport); 172 if (centerOnCursor) {
173 setCursorPosition(mCursorPosition.x, mCursorPosition.y);
174 } else {
175 // Find the new screen center (it was probably changed during the zo om operation) and
176 // update the viewport and cursor.
177 float[] mappedPoints = {
178 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)};
179 Matrix screenToImage = new Matrix();
180 mRenderData.transform.invert(screenToImage);
181 screenToImage.mapPoints(mappedPoints);
182 // The cursor is mapped to the center of the viewport in this case.
183 setCursorPosition(mappedPoints[0], mappedPoints[1]);
184 }
185 }
186
187 /**
188 * Repositions the image by translating it (without affecting the zoom level ).
189 */
190 private void repositionImage() {
191 // Map the current viewport position to screen coordinates and adjust th e image position.
192 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y};
193 mRenderData.transform.mapPoints(viewportPosition);
194
195 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0];
196 float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewport Position[1];
197
198 // Translate the image so the viewport center is displayed in the middle of the screen.
199 mRenderData.transform.postTranslate(viewportTransX, viewportTransY);
200
201 mRenderStub.setTransformation(mRenderData.transform);
202 }
203
204 /**
205 * Updates the given point such that it refers to a coordinate within the bo unds provided.
206 *
207 * @param point The point to adjust, the original object is modified.
208 * @param bounds The bounds used to constrain the point.
209 */
210 private void constrainPointToBounds(PointF point, RectF bounds) {
211 if (point.x < bounds.left) {
212 point.x = bounds.left;
213 } else if (point.x > bounds.right) {
214 point.x = bounds.right;
215 }
216
217 if (point.y < bounds.top) {
218 point.y = bounds.top;
219 } else if (point.y > bounds.bottom) {
220 point.y = bounds.bottom;
221 }
222 }
223
224 /** Returns a region which defines the set of valid cursor values in image s pace. */
225 private RectF getImageBounds() {
226 return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight);
227 }
228
229 /** Returns a region which defines the set of valid viewport center values i n image space. */
230 private RectF getViewportBounds() {
231 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight};
232 Matrix screenToImage = new Matrix();
233 mRenderData.transform.invert(screenToImage);
234 screenToImage.mapVectors(screenVectors);
235
236 float xAdjust = 0.0f;
237 if (mRenderData.imageWidth < screenVectors[0]) {
238 // Image is narrower than the screen, so adjust the bounds to center it.
239 xAdjust = (screenVectors[0] - mRenderData.imageWidth) / 2.0f;
240 }
241
242 float yAdjust = 0.0f;
243 if (mRenderData.imageHeight < screenVectors[1]) {
244 // Image is shorter than the screen, so adjust the bounds to center it.
245 yAdjust = (screenVectors[1] - mRenderData.imageHeight) / 2.0f;
246 }
247
248 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space.
249 float screenCenterX = (screenVectors[0] / 2.0f);
Lambros 2016/09/27 21:32:55 nit: () not needed.
joedow 2016/09/27 22:23:37 Done.
250 float screenCenterY = (screenVectors[1] / 2.0f);
251 return new RectF(screenCenterX - xAdjust, screenCenterY - yAdjust,
252 mRenderData.imageWidth - screenCenterX + xAdjust,
253 mRenderData.imageHeight - screenCenterY + yAdjust);
239 } 254 }
240 } 255 }
OLDNEW
« no previous file with comments | « no previous file | remoting/android/java/src/org/chromium/chromoting/TouchInputHandler.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698