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 * | |
15 * The transformation aspect is pretty simple, however the logic behind it is co mplex enough to | |
16 * warrant a longer chunk of comments to explain how that is accomplished. | |
17 * | |
18 * ===== Coordinate Systems ===== | |
19 * First, there are two coordinate spaces at play here, screen space and image s pace, which map to | |
20 * coordinates on the device's screen and to the image used to display the remot e desktop | |
21 * respectively. We can convert from image coordinates to screen coordinates by using the transform | |
22 * matrix held by |mRenderData| and convert from screen coordinates to image coo rdinates using the | |
23 * inverse of that matrix. | |
24 * | |
25 * For consistency, position (viewport and cursor) member variables store paddin g in image space as | |
26 * do the internal methods which modify them (unless otherwise noted). | |
27 * | |
28 * | |
29 * ===== Viewport Concept ===== | |
30 * Since the image used to display the remote desktop can be zoomed and panned s uch that only a | |
31 * portion of it is displayed on screen, this class defines that viewable area a s the "viewport". | |
32 * A goal of this class is to ensure the viewport contains the maximum amount of image pixels as it | |
33 * can. Thus it defines a subset of valid positions which map to the image, thes e are called | |
34 * boundaries or bounds. When a caller shifts the viewport, the new coordinate is checked to ensure | |
35 * it lies within the acceptable set of positions and is clamped to that viewabl e region if not. | |
36 * The viewport is tracked and bound by its center position. | |
37 * | |
38 * As an example, let's say the image is height 1000px and the screen height is 800 (both in screen | |
39 * coordinates). The viewport center would be located at 400px in this case and the viewport bounds | |
40 * would range from 400px to 600px. At 400px, the top of the image would align with the top of the | |
41 * screen and at 600px, the bottom of the image would align with the bottom of t he screen. | |
42 * | |
43 * | |
44 * ===== Cursor Position and Desired Viewport Concept ===== | |
45 * Moving the viewport position is simple and works well for direct input method s, such as Touch, | |
46 * however indirect methods, such as Trackpad mode, requires tracking of another position. This | |
47 * second position can be thought of as the 'desired' viewport position and coin cides with the | |
48 * cursor position. It is called the desired position because it is the origina l position of the | |
49 * viewport before the bounds are applied. This class attempts to keep the desi red and actual | |
50 * viewport centers at the same coordinate, however when the user can move the c ursor to a position | |
51 * which is outside of the allowed coordinates for the viewport (i.e. the edge o f the screen). | |
52 * We can't move the viewport center to that location without breaking our goal of maximizing the | |
53 * viewable area of the screen, so instead we track the cursor position and begi n shifting the | |
54 * viewport again once the cursor moves back into the region of allowable viewpo rt positions. | |
55 * | |
56 * | |
57 * ===== Letterbox Padding ===== | |
58 * One last note about the image and screen. When the image has been zoomed suc h that it is larger | |
59 * than the screen, we are free to move the viewport around as described above. However once the | |
60 * image is zoomed out such that one of the dimensions is smaller than the corre sponding screen | |
61 * dimension, we want to center the image within the viewport (and the screen). This is done by | |
62 * adding conceptual padding to the edges of the screen. As an example, when th e user zooms the | |
63 * image out and the height of the image (say 799px) drops below the screen heig ht (say 800px), we | |
64 * will add 0.5px to the top and bottom edges of the image in our position calcu lations. As the | |
65 * image height (in screen space) decreases (e.g. to 600px), the amount of paddi ng will increase. | |
66 * This ensures that the content is centered as the user zooms in and out. The same would occur | |
67 * if the image width (in screen space) dropped below the screen width with padd ing being added to | |
68 * the right and left edges of the image. This padding is referred to as "lette rbox" padding. | |
69 * Note that this padding is never applied to all four edges. The calculations used below allow one | |
70 * image dimension to decrease below the screen dimension but it prevents additi onal attempts to | |
71 * zoom out once the second image dimension matches the screen dimension. | |
Lambros
2016/10/03 23:04:10
I'm still struggling to understand this exact conc
joedow
2016/10/04 20:47:47
Discussed offline. I've simplifed the code a bit
| |
72 * | |
73 * | |
74 * ===== Viewport and System UI Concepts ===== | |
75 * System UI is defined as pixels displayed by the operating system. For us thi s boils down to four | |
76 * UI elements: status bar (located on the top edge of the screen), toolbar (loc ated below the | |
77 * status bar), navigation bar (located either at the bottom or right/left edge of the screen), and | |
78 * the soft input method (docked to bottom of the screen, possibly above the nav igation bar). | |
79 * | |
80 * Android has a mechanism to automatically resize the size of a UI View when Sy stem UI is displayed | |
81 * or changes size however this causes a jarring repositioning effect. We use t his mechanism in | |
82 * Pre-KitKat versions of Android as there isn't much of a choice. On KitKat an d later, we actually | |
83 * position the desktop image in a layer below the System UI so we can display t he image within the | |
84 * viewport in a consistent fashion and avoid those jarring reposition effects t hat occur on older | |
85 * OS versions. Though the experience is smoother, it also creates a new proble m which is how do | |
86 * users view and interact with remote UI elements on the image which are obscur ed by System UI. | |
87 * | |
88 * To address this problem, we allow the user to pan the remote image out from u nder the System UI. | |
89 * Our goal is to keep the viewport location consistent regardless of the System UI state whenever | |
90 * possible which leads to the following sections on padding and viewport positi on with System UI. | |
91 * | |
92 * | |
93 * ===== System UI Padding ===== | |
94 * In order to allow the user to pan the image out from under System UI, we need to know how much | |
95 * space that UI occupies on the screen. This information is provided by the Vi ew whenever the size | |
96 * changes. This information is applied to the viewport boundaries to allow the user to move the | |
97 * viewport further in that direction than they could have previously. | |
98 * | |
99 * If we use the same sizes as before (image is 1000px and screen is 800px in sc reen coordinates), | |
100 * the allowable viewport position values in the y axis are 400px through 600px. If System UI is | |
101 * shown at the top and bottom of the screen (say 50px along both edges) then we need to adjust the | |
102 * viewport to allow panning past that UI, the updated range of allowed values i s 350px to 650px. | |
103 * This adjustment allows the user to pan the desktop image out from under any S ystem UI and fixes | |
104 * the original problem. | |
105 * | |
106 * There are two caveats though. The first is how to handle the case when the S ystem UI changes | |
107 * size or disappears. The second is a complication due to letterbox padding. We'll address each | |
108 * in their own sections. | |
109 * | |
110 * | |
111 * ===== System UI and Letterbox Padding Overlap ===== | |
112 * If we did not account for letterbox padding when adjusting the viewport bound s, the behavior | |
113 * described in the System UI Padding section would fall apart when the image si ze (in screen space) | |
114 * decreased below the screen size. The effect would be that the user could pan the desktop image | |
115 * out from under the System UI (good) and then continue panning a distance defi ned by the amount of | |
116 * letterbox padding added to that edge. Functionally this is OK, but visually it looks broken. We | |
117 * address this problem by accounting for the overlap between the System UI and Letterbox padding. | |
118 * | |
119 * For this example, we will consider a single dimension (e.g. height) and assum e a screen size of | |
120 * 800px, an image size (once again in screen space) of 600px, letterbox padding on top and bottom | |
121 * edges of 100px, and System UI at the top and bottom of the screen at 50px eac h. | |
122 * | |
123 * Without the System UI, the viewport center would be locked to 400px (the actu al center of the | |
124 * image + letterbox padding). When the System UI was displayed, the range woul d be changed by 50px | |
125 * to 350px to 450px. This would allow the user to pan the image canvas by 50 p x in either | |
126 * direction. The problem here is that this panning is not useful as none of th e image is obscured | |
127 * by the System UI. Therefore we account for the letterbox padding and see tha t no scrolling is | |
128 * required and keep the viewport locked at 400px. | |
129 * | |
130 * The previous example showed an example interaction between a small System UI and the viewport | |
131 * bounds with letterbox padding, now let's consider a large System UI. In this example let's use | |
132 * a Soft Input Method UI which is 400 px tall and anchored to the bottom of the screen. Using the | |
133 * previous values (without letterboxing), the viewport range would be adjusted to 400px - 800px. | |
134 * This allows the image to be fully seen by the user, but includes a 100px blac k edge between the | |
135 * content and the System UI. What is needed is to account for the letterbox pa dding here as well. | |
136 * We subtract the padding (100px) from the allowable range and end up with 400p x - 700px. The | |
Lambros
2016/10/03 23:04:10
Shouldn't this be 500px - 700px ?
joedow
2016/10/04 20:47:47
Comment block removed.
| |
137 * image is fully visible and no letterbox padding is displayed. | |
Lambros
2016/10/03 23:04:10
But the image isn't fully visible? It's 600px tall
joedow
2016/10/04 20:47:47
Comment block removed.
| |
138 * | |
139 * | |
140 * ===== Image Padding and Ratcheting ===== | |
141 * On to the problem of consistency. The sections above have solved the visibil ity problem, with | |
142 * the methods described above, the image can always be viewed and all coordinat es on the image can | |
143 * be interacted with using the cursor or touch. So now we move on to the probl em of handling the | |
144 * boundaries when the System UI changes. | |
145 * | |
146 * First a description of the behavior we desire in this scenario. Let's say th e user has panned | |
147 * their image out from under the status and toolbar (~150px). When those UI el ements are dismissed | |
148 * we have two options, immediately trim the padding used for it and snap the vi ewport back to its | |
149 * normal position or allow the viewport to remain until the extra padding is no longer required. | |
Lambros
2016/10/03 23:04:10
"extra padding"? The amount of padding is *less* w
joedow
2016/10/04 20:47:47
N/A Comment block removed.
| |
150 * We have chosen the second approach. | |
151 * | |
152 * The idea is that after the System UI has been dismissed, we allow the viewpor t to remain as-is, | |
153 * but we don't want to keep this padding forever. The compromise is to 'eat' t he unneeded padding | |
154 * as the user pans the canvas, effectively creating a ratcheting effect. | |
155 * | |
156 * We accomplish this goal using a concept of image padding which tracks the act ual amount of | |
157 * padding in use for viewport calculations. In order to determine how much pad ding to use, we look | |
158 * at the adjusted System UI padding, current image padding, and the number of v isible pixels. | |
Lambros
2016/10/03 23:04:10
I don't understand "visible pixels" or the differe
joedow
2016/10/04 20:47:47
Comment block removed.
| |
159 * | |
160 * More examples would be useful here. Instead of talking about a dimension, le t's focus on a | |
161 * single edge for this set of example scenarios. Everything starts with System UI being displayed, | |
162 * otherwise there is no need for padding. The System UI for this example has a lready been adjusted | |
Lambros
2016/10/03 23:04:10
"otherwise there is no need for padding"
But lette
joedow
2016/10/04 20:47:47
Comment block removed.
| |
163 * to account for letterbox padding and totals 50px. | |
164 * Note: The Visible Pixels are also adjusted to account for letterbox padding. | |
165 * | |
166 * Example: No padding is ever used in viewport calculations. | |
167 * The user pans the image but never moves the image out from under the System UI. | |
168 * Edge Calculation: System UI: 50px, Image Padding: 0px, Visible Pixels 0px | |
169 * Edge Padding result: 0px | |
170 * The System UI then disappears: | |
171 * Edge Calculation: System UI: 0px, Image Padding: 0px, Visible Pixels 0px | |
172 * Edge Padding result: 0px | |
173 * | |
174 * Example: Some padding is used in viewport calculations. | |
175 * The user pans the image halfway out from under the System UI. | |
176 * Edge Calculation: System UI: 50px, Image Padding: 0px, Visible Pixels 25px | |
177 * Edge Padding result: 25px | |
178 * The System UI then disappears (the viewport remains stationary): | |
179 * Edge Calculation: System UI: 0px, Image Padding: 25px, Visible Pixels 25px | |
180 * Edge Padding result: 25px | |
181 * The user pans away from the edge a bit (say 10px): | |
182 * Edge Calculation: System UI: 0px, Image Padding: 25px, Visible Pixels 15px | |
183 * Edge Padding result: 15px | |
184 * The user attempts to pan back toward the edge but cannot go past the 15px p adding. | |
185 * The user then pans 75px away from the edge: | |
186 * Edge Calculation: System UI: 0px, Image Padding: 15px, Visible Pixels -75px | |
187 * Edge Padding result: 0px | |
188 * | |
189 * | |
190 * ===== Image Padding and Zooming ===== | |
191 * In the previous section, the ratcheting mechanism was discussed which reduces the padding when | |
192 * the user pans away from the edge of the screen. However what happens when a user zooms when | |
193 * padding is applied? If the user is zooming in, then nothing is required as t he image will be | |
194 * expanded and the padding will be 'absorbed' by the ratcheting effect describe d previously. The | |
195 * problem occurs when the user is zooming out. In that case, the edge of the i mage is not changed | |
196 * and the effect looks odd (but is functionally OK). | |
197 * | |
198 * The solution to this problem is to reduce the amount of padding by a constant factor. This | |
199 * reduction creates an easing effect as the position of the image slides toward the edge as the | |
200 * padding is reduced. | |
201 * | |
202 * | |
203 * ===== Image Padding and Cursors ===== | |
204 * All of the problems and solutions above apply equally to Touch and Trackpad m odes. However | |
205 * Trackpad mode has two additional problems. | |
206 * | |
207 * The first is the problem of newly displayed System UI obscurring the cursor. Imagine you move | |
208 * the cursor to the upper corner of the screen and then display the toolbar. T he toolbar will be | |
209 * displayed over the top of the cursor which is not a good idea. Our goal is t o minimize screen | |
210 * translations, but in this case, it is more important that the user does not l ose sight of the | |
211 * cursor. Thus we automatically move the viewport so the cursor is guaranteed to be visible. If | |
212 * the cursor is centered on the screen, then no translation is needed. | |
213 * | |
214 * The second is also related to visibility but is not solved by the solution ab ove. For this case, | |
215 * let's assume the cursor is centered in the viewport. Next the user brings up the Soft Keyboard. | |
216 * The Keyboard's height is near that of the screen center so the cursor is obsc ured. The user is | |
217 * not able to see what they are interacting with and selecting a text box is im possible. For this | |
218 * scenario, we artificially shift the 'screen center' position by viewport_size / 2. This ensures | |
219 * the cursor is visible and the user can view and interact with any element on the remote desktop. | |
220 * | |
14 */ | 221 */ |
15 public class DesktopCanvas { | 222 public class DesktopCanvas { |
16 /** | 223 /** |
17 * Maximum allowed zoom level - see {@link #repositionImageWithZoom()}. | 224 * Maximum allowed zoom level - see {@link #scaleAndRepositionImage()}. |
18 */ | 225 */ |
19 private static final float MAX_ZOOM_FACTOR = 100.0f; | 226 private static final float MAX_ZOOM_FACTOR = 100.0f; |
20 | 227 |
228 /** | |
229 * Used to smoothly reduce the amount of padding while the user is zooming. | |
230 */ | |
231 private static final float PADDING_REDUCTION_FACTOR = 0.85f; | |
232 | |
21 private final RenderStub mRenderStub; | 233 private final RenderStub mRenderStub; |
22 private final RenderData mRenderData; | 234 private final RenderData mRenderData; |
23 | 235 |
24 /** | 236 /** |
25 * Represents the actual center of the viewport. This value needs to be a p air of floats so the | 237 * Represents the actual center of the viewport in image space. This value needs to be a pair |
26 * desktop image can be positioned with sub-pixel accuracy for smoother pann ing animations at | 238 * of floats so the desktop image can be positioned with sub-pixel accuracy for smoother panning |
27 * high zoom levels. | 239 * animations at high zoom levels. |
28 */ | 240 */ |
29 private PointF mViewportPosition = new PointF(); | 241 private PointF mViewportPosition = new PointF(); |
30 | 242 |
31 /** | 243 /** |
32 * Represents the desired center of the viewport. This value may not repres ent the actual | 244 * Represents the desired center of the viewport in image space. This value may not represent |
33 * center of the viewport as adjustments are made to ensure as much of the d esktop is visible as | 245 * the actual center of the viewport as adjustments are made to ensure as mu ch of the desktop is |
34 * possible. This value needs to be a pair of floats so the desktop image c an be positioned | 246 * visible as possible. This value needs to be a pair of floats so the desk top image can be |
35 * with sub-pixel accuracy for smoother panning animations at high zoom leve ls. | 247 * positioned with sub-pixel accuracy for smoother panning animations at hig h zoom levels. |
36 */ | 248 */ |
37 private PointF mCursorPosition = new PointF(); | 249 private PointF mCursorPosition = new PointF(); |
38 | 250 |
39 /** | 251 /** |
40 * Represents the amount of space, in pixels, used by System UI. | 252 * Represents the amount of space, in screen pixels, used by System UI. |
41 */ | 253 */ |
42 private Rect mSystemUiOffsetPixels = new Rect(); | 254 private Rect mSystemUiOffsetPixels = new Rect(); |
43 | 255 |
256 /** | |
257 * Represents the additional space, in screen pixels, for each edge of the d esktop image. | |
258 * Used to allow the user to position the image canvas out from under any vi sible System UI. | |
259 */ | |
260 private RectF mImagePadding = new RectF(); | |
261 | |
44 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { | 262 public DesktopCanvas(RenderStub renderStub, RenderData renderData) { |
45 mRenderStub = renderStub; | 263 mRenderStub = renderStub; |
46 mRenderData = renderData; | 264 mRenderData = renderData; |
47 } | 265 } |
48 | 266 |
49 /** | 267 /** |
50 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures | 268 * Sets the desired center position of the viewport (a.k.a. the cursor posit ion) and ensures |
51 * the viewport is updated to include the cursor within it. | 269 * the viewport is updated to include the cursor within it. |
52 * | 270 * |
53 * @param newX The new x coordinate value for the desired center position. | 271 * @param newX The new x coordinate value for the desired center position. |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
162 mRenderData.transform.mapVectors(imageSize); | 380 mRenderData.transform.mapVectors(imageSize); |
163 | 381 |
164 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { | 382 if (imageSize[0] < mRenderData.screenWidth && imageSize[1] < mRenderData .screenHeight) { |
165 // Displayed image is too small in both directions, so apply the min imum zoom | 383 // Displayed image is too small in both directions, so apply the min imum zoom |
166 // level needed to fit either the width or height. | 384 // level needed to fit either the width or height. |
167 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, | 385 float scale = Math.min((float) mRenderData.screenWidth / mRenderData .imageWidth, |
168 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); | 386 (float) mRenderData.screenHeight / mRenderDat a.imageHeight); |
169 mRenderData.transform.setScale(scale, scale); | 387 mRenderData.transform.setScale(scale, scale); |
170 } | 388 } |
171 | 389 |
390 // Trim the image padding if the user is zooming out. This prevents cas es where the image | |
391 // pops to the center when it reaches its minimum size. Note that we do not need to do | |
392 // anything when the user is zooming in as the canvas will expand and ab sorb the padding. | |
393 if (scaleFactor < 1.0f) { | |
394 mImagePadding.set(mImagePadding.left * PADDING_REDUCTION_FACTOR, | |
395 mImagePadding.top * PADDING_REDUCTION_FACTOR, | |
396 mImagePadding.right * PADDING_REDUCTION_FACTOR, | |
397 mImagePadding.bottom * PADDING_REDUCTION_FACTOR); | |
398 } | |
399 | |
172 if (centerOnCursor) { | 400 if (centerOnCursor) { |
173 setCursorPosition(mCursorPosition.x, mCursorPosition.y); | 401 setCursorPosition(mCursorPosition.x, mCursorPosition.y); |
174 } else { | 402 } else { |
175 // Find the new screen center (it was probably changed during the zo om operation) and | 403 // Find the new screen center (it was probably changed during the zo om operation) and |
176 // update the viewport and cursor. | 404 // update the viewport and cursor. |
177 float[] mappedPoints = { | 405 float[] mappedPoints = { |
178 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)}; | 406 ((float) mRenderData.screenWidth / 2), ((float) mRenderData. screenHeight / 2)}; |
179 Matrix screenToImage = new Matrix(); | 407 Matrix screenToImage = new Matrix(); |
180 mRenderData.transform.invert(screenToImage); | 408 mRenderData.transform.invert(screenToImage); |
181 screenToImage.mapPoints(mappedPoints); | 409 screenToImage.mapPoints(mappedPoints); |
182 // The cursor is mapped to the center of the viewport in this case. | 410 // The cursor is mapped to the center of the viewport in this case. |
183 setCursorPosition(mappedPoints[0], mappedPoints[1]); | 411 setCursorPosition(mappedPoints[0], mappedPoints[1]); |
184 } | 412 } |
185 } | 413 } |
186 | 414 |
187 /** | 415 /** |
188 * Repositions the image by translating it (without affecting the zoom level ). | 416 * Repositions the image by translating it (without affecting the zoom level ). |
189 */ | 417 */ |
190 private void repositionImage() { | 418 private void repositionImage() { |
191 // Map the current viewport position to screen coordinates and adjust th e image position. | 419 // Map the current viewport position to screen coordinates and adjust th e image position. |
192 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; | 420 float[] viewportPosition = {mViewportPosition.x, mViewportPosition.y}; |
193 mRenderData.transform.mapPoints(viewportPosition); | 421 mRenderData.transform.mapPoints(viewportPosition); |
194 | 422 |
195 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; | 423 float viewportTransX = ((float) mRenderData.screenWidth / 2) - viewportP osition[0]; |
196 float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewport Position[1]; | 424 float viewportTransY = ((float) mRenderData.screenHeight / 2) - viewport Position[1]; |
197 | 425 |
198 // Translate the image so the viewport center is displayed in the middle of the screen. | 426 // Translate the image so the viewport center is displayed in the middle of the screen. |
199 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); | 427 mRenderData.transform.postTranslate(viewportTransX, viewportTransY); |
200 | 428 |
429 adjustImagePadding(); | |
430 | |
201 mRenderStub.setTransformation(mRenderData.transform); | 431 mRenderStub.setTransformation(mRenderData.transform); |
202 } | 432 } |
203 | 433 |
204 /** | 434 /** |
205 * Updates the given point such that it refers to a coordinate within the bo unds provided. | 435 * Updates the given point such that it refers to a coordinate within the bo unds provided. |
206 * | 436 * |
207 * @param point The point to adjust, the original object is modified. | 437 * @param point The point to adjust, the original object is modified. |
208 * @param bounds The bounds used to constrain the point. | 438 * @param bounds The bounds used to constrain the point. |
209 */ | 439 */ |
210 private void constrainPointToBounds(PointF point, RectF bounds) { | 440 private void constrainPointToBounds(PointF point, RectF bounds) { |
211 if (point.x < bounds.left) { | 441 if (point.x < bounds.left) { |
212 point.x = bounds.left; | 442 point.x = bounds.left; |
213 } else if (point.x > bounds.right) { | 443 } else if (point.x > bounds.right) { |
214 point.x = bounds.right; | 444 point.x = bounds.right; |
215 } | 445 } |
216 | 446 |
217 if (point.y < bounds.top) { | 447 if (point.y < bounds.top) { |
218 point.y = bounds.top; | 448 point.y = bounds.top; |
219 } else if (point.y > bounds.bottom) { | 449 } else if (point.y > bounds.bottom) { |
220 point.y = bounds.bottom; | 450 point.y = bounds.bottom; |
221 } | 451 } |
222 } | 452 } |
223 | 453 |
224 /** Returns a region which defines the set of valid cursor values in image s pace. */ | 454 /** Returns a region used to constrain the cursor position in image space. * / |
225 private RectF getImageBounds() { | 455 private RectF getImageBounds() { |
226 return new RectF(0, 0, mRenderData.imageWidth, mRenderData.imageHeight); | 456 // Expand the allowable cursor positions to include any applicable paddi ng (includes both |
457 // unused System UI padding and currently used image padding). Doing th is allows the user | |
458 // to move their cursor into this space to indicate they want to scroll it into the viewable | |
459 // area. | |
460 RectF systemUiOffsetPixels = getAdjustedSystemUiOffsets(); | |
461 float[] paddingBuffer = {Math.max(mImagePadding.left, systemUiOffsetPixe ls.left), | |
462 Math.max(mImagePadding.top, systemUiOffsetPixels.top), | |
463 Math.max(mImagePadding.right, systemUiOffsetPixels.right), | |
464 Math.max(mImagePadding.bottom, systemUiOffsetPixels.bottom)}; | |
465 Matrix screenToImage = new Matrix(); | |
466 mRenderData.transform.invert(screenToImage); | |
467 screenToImage.mapVectors(paddingBuffer); | |
468 | |
469 return new RectF(-paddingBuffer[0], -paddingBuffer[1], | |
470 mRenderData.imageWidth + paddingBuffer[2], | |
471 mRenderData.imageHeight + paddingBuffer[3]); | |
227 } | 472 } |
228 | 473 |
229 /** Returns a region which defines the set of valid viewport center values i n image space. */ | 474 /** Returns a region which defines the set of valid viewport center values i n image space. */ |
230 private RectF getViewportBounds() { | 475 private RectF getViewportBounds() { |
231 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; | 476 float[] screenVectors = {(float) mRenderData.screenWidth, (float) mRende rData.screenHeight}; |
232 Matrix screenToImage = new Matrix(); | 477 Matrix screenToImage = new Matrix(); |
233 mRenderData.transform.invert(screenToImage); | 478 mRenderData.transform.invert(screenToImage); |
234 screenToImage.mapVectors(screenVectors); | 479 screenToImage.mapVectors(screenVectors); |
235 | 480 |
236 float xAdjust = 0.0f; | 481 PointF letterBoxPadding = getLetterboxPaddingOnScreen(); |
237 if (mRenderData.imageWidth < screenVectors[0]) { | 482 float[] letterBoxPaddingVectors = {letterBoxPadding.x, letterBoxPadding. y}; |
238 // Image is narrower than the screen, so adjust the bounds to center it. | 483 screenToImage.mapVectors(letterBoxPaddingVectors); |
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 | 484 |
248 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. | 485 // screenCenter values are 1/2 of a particular screen dimension mapped t o image space. |
249 float screenCenterX = screenVectors[0] / 2.0f; | 486 float screenCenterX = (screenVectors[0] / 2.0f) - letterBoxPaddingVector s[0]; |
250 float screenCenterY = screenVectors[1] / 2.0f; | 487 float screenCenterY = (screenVectors[1] / 2.0f) - letterBoxPaddingVector s[1]; |
251 return new RectF(screenCenterX - xAdjust, screenCenterY - yAdjust, | 488 RectF imageBounds = getImageBounds(); |
252 mRenderData.imageWidth - screenCenterX + xAdjust, | 489 imageBounds.inset(screenCenterX, screenCenterY); |
253 mRenderData.imageHeight - screenCenterY + yAdjust); | 490 return imageBounds; |
491 } | |
492 | |
493 /** Returns the letterbox padding needed in each dimension (x and y) in scre en coordinates. */ | |
494 private PointF getLetterboxPaddingOnScreen() { | |
495 float[] imageVectors = {mRenderData.imageWidth, mRenderData.imageHeight} ; | |
496 mRenderData.transform.mapVectors(imageVectors); | |
497 | |
498 // We want to letterbox when the image is smaller than the screen in a s pecific dimension. | |
499 // Since we center the image, split the difference so it is equally dist ributed. | |
500 float widthAdjust = | |
501 Math.max(0.0f, ((float) mRenderData.screenWidth - imageVectors[0 ]) / 2.0f); | |
502 float heightAdjust = | |
503 Math.max(0.0f, ((float) mRenderData.screenHeight - imageVectors[ 1]) / 2.0f); | |
504 | |
505 return new PointF(widthAdjust, heightAdjust); | |
506 } | |
507 | |
508 /** | |
509 * Returns the amount of System UI, adjusted for letterbox padding overlap, to use for padding. | |
510 */ | |
511 private RectF getAdjustedSystemUiOffsets() { | |
512 // Ignore negative padding since that means no adjustment is necessary. We use a floor of | |
513 // 0.0f to simplify calculations using the returned values. | |
514 PointF letterBoxPadding = getLetterboxPaddingOnScreen(); | |
515 return new RectF(Math.max(mSystemUiOffsetPixels.left - letterBoxPadding. x, 0.0f), | |
516 Math.max(mSystemUiOffsetPixels.top - letterBoxPadding.y, 0.0f), | |
517 Math.max(mSystemUiOffsetPixels.right - letterBoxPadding.x, 0.0f) , | |
518 Math.max(mSystemUiOffsetPixels.bottom - letterBoxPadding.y, 0.0f )); | |
519 } | |
520 | |
521 /** | |
522 * Updates the padding used to allow the user to scroll the image out from u nder System UI. | |
523 * This is done by taking the adjusted System UI size, current padding, and visible pixels for | |
524 * each edge of the screen. | |
525 */ | |
526 private void adjustImagePadding() { | |
527 PointF letterBoxPadding = getLetterboxPaddingOnScreen(); | |
528 RectF systemUiOffsetPixels = getAdjustedSystemUiOffsets(); | |
529 float[] imagePoints = {0.0f, 0.0f, mRenderData.imageWidth, mRenderData.i mageHeight}; | |
530 mRenderData.transform.mapPoints(imagePoints); | |
531 | |
532 mImagePadding.set( | |
533 adjustEdgePadding(systemUiOffsetPixels.left, mImagePadding.left, | |
534 imagePoints[0] - letterBoxPadding.x), | |
535 adjustEdgePadding(systemUiOffsetPixels.top, mImagePadding.top, | |
536 imagePoints[1] - letterBoxPadding.y), | |
537 adjustEdgePadding(systemUiOffsetPixels.right, mImagePadding.righ t, | |
538 mRenderData.screenWidth - imagePoints[2] - letterBoxPadd ing.x), | |
539 adjustEdgePadding(systemUiOffsetPixels.bottom, mImagePadding.bot tom, | |
540 mRenderData.screenHeight - imagePoints[3] - letterBoxPad ding.y)); | |
541 } | |
542 | |
543 /** | |
544 * Update the current edge padding based on the current System UI and image position state. | |
545 * | |
546 * @param systemUiPadding Amount of visible System UI in pixels. | |
547 * @param currentPadding Amount of padding used, in pixels, for position cal culations. | |
548 * @param visiblePixels Amount of padding actually visible, in pixels. | |
549 * @return The new value, in pixels, to be used for positioning. | |
550 */ | |
551 private float adjustEdgePadding( | |
552 float systemUiPadding, float currentPadding, float visiblePixels) { | |
553 float edgeValue; | |
554 visiblePixels = Math.max(visiblePixels, 0.0f); | |
555 if (systemUiPadding > 0.0f) { | |
556 if (systemUiPadding > currentPadding) { | |
557 // Add enough padding to allow scrolling the desktop out from un der the System UI. | |
558 edgeValue = Math.min(systemUiPadding, visiblePixels); | |
559 } else if (systemUiPadding < currentPadding) { | |
560 // Remove unneeded padding from the canvas. | |
561 edgeValue = Math.max(systemUiPadding, Math.min(currentPadding, v isiblePixels)); | |
562 } else { | |
563 // If current and system padding sizes are the same, then keep t he visible portion. | |
564 edgeValue = Math.min(currentPadding, visiblePixels); | |
565 } | |
566 } else if (currentPadding > 0.0f) { | |
567 // If we have existing padding, then trim if possible, otherwise kee p the same value. | |
568 edgeValue = Math.min(currentPadding, visiblePixels); | |
569 } else { | |
570 // Use zero padding if there is no system or current padding specifi ed. | |
571 edgeValue = 0.0f; | |
572 } | |
573 return edgeValue; | |
254 } | 574 } |
255 } | 575 } |
OLD | NEW |