Chromium Code Reviews| 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 |