OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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.content.Context; | 7 import android.content.Context; |
8 import android.graphics.Bitmap; | 8 import android.graphics.Bitmap; |
9 import android.graphics.Canvas; | 9 import android.graphics.Canvas; |
10 import android.graphics.Color; | 10 import android.graphics.Color; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 | 50 |
51 /** Stores pan and zoom configuration and converts image coordinates to scre
en coordinates. */ | 51 /** Stores pan and zoom configuration and converts image coordinates to scre
en coordinates. */ |
52 private Matrix mTransform; | 52 private Matrix mTransform; |
53 | 53 |
54 private int mScreenWidth; | 54 private int mScreenWidth; |
55 private int mScreenHeight; | 55 private int mScreenHeight; |
56 | 56 |
57 /** Specifies the dimension by which the zoom level is being lower-bounded.
*/ | 57 /** Specifies the dimension by which the zoom level is being lower-bounded.
*/ |
58 private Constraint mConstraint; | 58 private Constraint mConstraint; |
59 | 59 |
| 60 /** Whether the right edge of the image was visible on-screen during the las
t render. */ |
| 61 private boolean mRightUsedToBeOut; |
| 62 |
| 63 /** Whether the bottom edge of the image was visible on-screen during the la
st render. */ |
| 64 private boolean mBottomUsedToBeOut; |
| 65 |
60 /** Whether the device has just been rotated, necessitating a canvas redraw.
*/ | 66 /** Whether the device has just been rotated, necessitating a canvas redraw.
*/ |
61 private boolean mJustRotated; | 67 private boolean mJustRotated; |
62 | 68 |
63 public DesktopView(Context context) { | 69 public DesktopView(Context context) { |
64 super(context); | 70 super(context); |
65 getHolder().addCallback(this); | 71 getHolder().addCallback(this); |
66 DesktopListener listener = new DesktopListener(); | 72 DesktopListener listener = new DesktopListener(); |
67 mScroller = new GestureDetector(context, listener); | 73 mScroller = new GestureDetector(context, listener); |
68 mZoomer = new ScaleGestureDetector(context, listener); | 74 mZoomer = new ScaleGestureDetector(context, listener); |
69 | 75 |
70 mTransform = new Matrix(); | 76 mTransform = new Matrix(); |
71 mScreenWidth = 0; | 77 mScreenWidth = 0; |
72 mScreenHeight = 0; | 78 mScreenHeight = 0; |
73 mConstraint = Constraint.UNDEFINED; | 79 mConstraint = Constraint.UNDEFINED; |
| 80 |
| 81 mRightUsedToBeOut = false; |
| 82 mBottomUsedToBeOut = false; |
| 83 |
| 84 mJustRotated = false; |
74 } | 85 } |
75 | 86 |
76 /** | 87 /** |
77 * Redraws the canvas. This should be done on a non-UI thread or it could | 88 * Redraws the canvas. This should be done on a non-UI thread or it could |
78 * cause the UI to lag. Specifically, it is currently invoked on the native | 89 * cause the UI to lag. Specifically, it is currently invoked on the native |
79 * graphics thread using a JNI. | 90 * graphics thread using a JNI. |
80 */ | 91 */ |
81 @Override | 92 @Override |
82 public void run() { | 93 public void run() { |
83 if (Looper.myLooper()==Looper.getMainLooper()) { | 94 if (Looper.myLooper() == Looper.getMainLooper()) { |
84 Log.w("deskview", "Canvas being redrawn on UI thread"); | 95 Log.w("deskview", "Canvas being redrawn on UI thread"); |
85 } | 96 } |
86 | 97 |
| 98 Bitmap image = JniInterface.retrieveVideoFrame(); |
87 Canvas canvas = getHolder().lockCanvas(); | 99 Canvas canvas = getHolder().lockCanvas(); |
88 Bitmap image = JniInterface.retrieveVideoFrame(); | |
89 synchronized (mTransform) { | 100 synchronized (mTransform) { |
90 canvas.setMatrix(mTransform); | 101 canvas.setMatrix(mTransform); |
91 | 102 |
| 103 // Internal parameters of the transformation matrix. |
92 float[] values = new float[9]; | 104 float[] values = new float[9]; |
93 mTransform.getValues(values); | 105 mTransform.getValues(values); |
| 106 |
| 107 // Screen coordinates of two defining points of the image. |
94 float[] topleft = {0, 0}; | 108 float[] topleft = {0, 0}; |
95 mTransform.mapPoints(topleft); | 109 mTransform.mapPoints(topleft); |
96 float[] bottomright = {image.getWidth(), image.getHeight()}; | 110 float[] bottomright = {image.getWidth(), image.getHeight()}; |
97 mTransform.mapPoints(bottomright); | 111 mTransform.mapPoints(bottomright); |
98 | 112 |
99 if (mConstraint==Constraint.UNDEFINED) { | 113 // Whether to rescale and recenter the view. |
| 114 boolean recenter = false; |
| 115 |
| 116 if (mConstraint == Constraint.UNDEFINED) { |
100 mConstraint = image.getWidth()/image.getHeight() > mScreenWidth/
mScreenHeight ? | 117 mConstraint = image.getWidth()/image.getHeight() > mScreenWidth/
mScreenHeight ? |
101 Constraint.HEIGHT : Constraint.WIDTH; | 118 Constraint.WIDTH : Constraint.HEIGHT; |
| 119 recenter = true; // We always rescale and recenter after a rota
tion. |
102 } | 120 } |
103 | 121 |
104 if (mConstraint==Constraint.WIDTH && bottomright[0] - topleft[0] < m
ScreenWidth) { | 122 if (mConstraint == Constraint.WIDTH && |
105 mTransform.setPolyToPoly(new float[] {0, 0, image.getWidth(), 0}
, 0, | 123 ((int)(bottomright[0] - topleft[0] + 0.5) < mScreenWidth ||
recenter)) { |
106 new float[] {0, 0, mScreenWidth, 0}, 0, 2); | 124 // The vertical edges of the image are flush against the device'
s screen edges |
107 } else if (mConstraint==Constraint.HEIGHT && | 125 // when the entire host screen is visible, and the user has zoom
ed out too far. |
108 bottomright[1] - topleft[1] < mScreenHeight) { | 126 float imageMiddle = (float)image.getHeight() / 2; |
109 mTransform.setPolyToPoly(new float[] {0, 0, 0, image.getHeight()
}, 0, | 127 float screenMiddle = (float)mScreenHeight / 2; |
110 new float[] {0, 0, 0, mScreenHeight}, 0, 2); | 128 mTransform.setPolyToPoly( |
| 129 new float[] {0, imageMiddle, image.getWidth(), imageMidd
le}, 0, |
| 130 new float[] {0, screenMiddle, mScreenWidth, screenMiddle
}, 0, 2); |
| 131 } else if (mConstraint == Constraint.HEIGHT && |
| 132 ((int)(bottomright[1] - topleft[1] + 0.5) < mScreenHeight ||
recenter)) { |
| 133 // The horizontal image edges are flush against the device's scr
een edges when |
| 134 // the entire host screen is visible, and the user has zoomed ou
t too far. |
| 135 float imageCenter = (float)image.getWidth() / 2; |
| 136 float screenCenter = (float)mScreenWidth / 2; |
| 137 mTransform.setPolyToPoly( |
| 138 new float[] {imageCenter, 0, imageCenter, image.getHeigh
t()}, 0, |
| 139 new float[] {screenCenter, 0, screenCenter, mScreenHeigh
t}, 0, 2); |
111 } else { | 140 } else { |
112 if (values[Matrix.MTRANS_X] > 0) { | 141 // It's fine for both members of a pair of image edges to be wit
hin the screen |
113 values[Matrix.MTRANS_X] = 0; | 142 // edges (or "out of bounds"); that simply means that the image
is zoomed out as |
| 143 // far as permissible. And both members of a pair can obviously
be outside the |
| 144 // screen's edges, which indicates that the image is zoomed in t
o far to see the |
| 145 // whole host screen. However, if only one of a pair of edges ha
s entered the |
| 146 // screen, the user is attempting to scroll into a blank area of
the canvas. |
| 147 |
| 148 // A value of true means the corresponding edge has entered the
screen's borders. |
| 149 boolean leftEdgeOutOfBounds = values[Matrix.MTRANS_X] > 0; |
| 150 boolean topEdgeOutOfBounds = values[Matrix.MTRANS_Y] > 0; |
| 151 boolean rightEdgeOutOfBounds = bottomright[0] < mScreenWidth; |
| 152 boolean bottomEdgeOutOfBounds = bottomright[1] < mScreenHeight; |
| 153 |
| 154 if (leftEdgeOutOfBounds != rightEdgeOutOfBounds) { |
| 155 if (leftEdgeOutOfBounds != mRightUsedToBeOut) { |
| 156 values[Matrix.MTRANS_X] = 0; |
| 157 } |
| 158 else { |
| 159 values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0]
; |
| 160 } |
114 } | 161 } |
115 if (values[Matrix.MTRANS_Y] > 0) { | 162 else { // The view would oscillate if this were updated while s
crolling off-screen. |
116 values[Matrix.MTRANS_Y] = 0; | 163 mRightUsedToBeOut = rightEdgeOutOfBounds; |
117 } | 164 } |
118 if (bottomright[0] < mScreenWidth) { | 165 |
119 values[Matrix.MTRANS_X] += mScreenWidth - bottomright[0]; | 166 if (topEdgeOutOfBounds != bottomEdgeOutOfBounds) { |
| 167 if (topEdgeOutOfBounds != mBottomUsedToBeOut) { |
| 168 values[Matrix.MTRANS_Y] = 0; |
| 169 } |
| 170 else { |
| 171 values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1
]; |
| 172 } |
120 } | 173 } |
121 if (bottomright[1] < mScreenHeight) { | 174 else { // The view would oscillate if this were updated while s
crolling off-screen. |
122 values[Matrix.MTRANS_Y] += mScreenHeight - bottomright[1]; | 175 mBottomUsedToBeOut = bottomEdgeOutOfBounds; |
123 } | 176 } |
124 | 177 |
125 mTransform.setValues(values); | 178 mTransform.setValues(values); |
126 } | 179 } |
127 | 180 |
128 canvas.setMatrix(mTransform); | 181 canvas.setMatrix(mTransform); |
129 } | 182 } |
130 | 183 |
131 canvas.drawColor(Color.BLACK); | 184 canvas.drawColor(Color.BLACK); |
132 canvas.drawBitmap(image, 0, 0, new Paint()); | 185 canvas.drawBitmap(image, 0, 0, new Paint()); |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 | 283 |
231 /** | 284 /** |
232 * Called when the user is done zooming. Defers to onScale()'s judgement
. | 285 * Called when the user is done zooming. Defers to onScale()'s judgement
. |
233 */ | 286 */ |
234 @Override | 287 @Override |
235 public void onScaleEnd(ScaleGestureDetector detector) { | 288 public void onScaleEnd(ScaleGestureDetector detector) { |
236 onScale(detector); | 289 onScale(detector); |
237 } | 290 } |
238 } | 291 } |
239 } | 292 } |
OLD | NEW |