• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.util;
18 
19 import static android.view.Surface.ROTATION_0;
20 import static android.view.Surface.ROTATION_180;
21 import static android.view.Surface.ROTATION_270;
22 import static android.view.Surface.ROTATION_90;
23 
24 import android.annotation.Dimension;
25 import android.graphics.Insets;
26 import android.graphics.Matrix;
27 import android.graphics.Point;
28 import android.graphics.PointF;
29 import android.graphics.Rect;
30 import android.view.Surface;
31 import android.view.Surface.Rotation;
32 import android.view.SurfaceControl;
33 
34 /**
35  * A class containing utility methods related to rotation.
36  *
37  * @hide
38  */
39 public class RotationUtils {
40 
41     /**
42      * Rotates an Insets according to the given rotation.
43      */
rotateInsets(Insets insets, @Rotation int rotation)44     public static Insets rotateInsets(Insets insets, @Rotation int rotation) {
45         if (insets == null || insets == Insets.NONE) {
46             return insets;
47         }
48         Insets rotated;
49         switch (rotation) {
50             case ROTATION_0:
51                 rotated = insets;
52                 break;
53             case ROTATION_90:
54                 rotated = Insets.of(
55                         insets.top,
56                         insets.right,
57                         insets.bottom,
58                         insets.left);
59                 break;
60             case ROTATION_180:
61                 rotated = Insets.of(
62                         insets.right,
63                         insets.bottom,
64                         insets.left,
65                         insets.top);
66                 break;
67             case ROTATION_270:
68                 rotated = Insets.of(
69                         insets.bottom,
70                         insets.left,
71                         insets.top,
72                         insets.right);
73                 break;
74             default:
75                 throw new IllegalArgumentException("unknown rotation: " + rotation);
76         }
77         return rotated;
78     }
79 
80     /**
81      * Rotates bounds as if parentBounds and bounds are a group. The group is rotated from
82      * oldRotation to newRotation. This assumes that parentBounds is at 0,0 and remains at 0,0 after
83      * rotation. The bounds will be at the same physical position in parentBounds.
84      *
85      * Only 'inOutBounds' is mutated.
86      */
rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation, @Rotation int newRotation)87     public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation,
88             @Rotation int newRotation) {
89         rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation));
90     }
91 
92     /**
93      * Rotates inOutBounds together with the parent for a given rotation delta. This assumes that
94      * the parent starts at 0,0 and remains at 0,0 after the rotation. The inOutBounds will remain
95      * at the same physical position within the parent.
96      *
97      * Only 'inOutBounds' is mutated.
98      */
rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight, @Rotation int rotation)99     public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
100             @Rotation int rotation) {
101         final int origLeft = inOutBounds.left;
102         final int origTop = inOutBounds.top;
103         switch (rotation) {
104             case ROTATION_0:
105                 return;
106             case ROTATION_90:
107                 inOutBounds.left = inOutBounds.top;
108                 inOutBounds.top = parentWidth - inOutBounds.right;
109                 inOutBounds.right = inOutBounds.bottom;
110                 inOutBounds.bottom = parentWidth - origLeft;
111                 return;
112             case ROTATION_180:
113                 inOutBounds.left = parentWidth - inOutBounds.right;
114                 inOutBounds.right = parentWidth - origLeft;
115                 inOutBounds.top = parentHeight - inOutBounds.bottom;
116                 inOutBounds.bottom = parentHeight - origTop;
117                 return;
118             case ROTATION_270:
119                 inOutBounds.left = parentHeight - inOutBounds.bottom;
120                 inOutBounds.bottom = inOutBounds.right;
121                 inOutBounds.right = parentHeight - inOutBounds.top;
122                 inOutBounds.top = origLeft;
123         }
124     }
125 
126     /**
127      * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
128      * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
129      * remains at 0,0 after rotation. The bounds will be at the same physical position in
130      * parentBounds.
131      *
132      * Only 'inOutBounds' is mutated.
133      */
rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation)134     public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
135         rotateBounds(inOutBounds, parentBounds.right, parentBounds.bottom, rotation);
136     }
137 
138     /** @return the rotation needed to rotate from oldRotation to newRotation. */
139     @Rotation
deltaRotation(@otation int oldRotation, @Rotation int newRotation)140     public static int deltaRotation(@Rotation int oldRotation, @Rotation int newRotation) {
141         int delta = newRotation - oldRotation;
142         if (delta < 0) delta += 4;
143         return delta;
144     }
145 
146     /**
147      * Rotates a surface CCW around the origin (eg. a 90-degree rotation will result in the
148      * bottom-left being at the origin). Use {@link #rotatePoint} to transform the top-left
149      * corner appropriately.
150      */
rotateSurface(SurfaceControl.Transaction t, SurfaceControl sc, @Rotation int rotation)151     public static void rotateSurface(SurfaceControl.Transaction t, SurfaceControl sc,
152             @Rotation int rotation) {
153         // Note: the matrix values look inverted, but they aren't because our coordinate-space
154         // is actually left-handed.
155         // Note: setMatrix expects values in column-major order.
156         switch (rotation) {
157             case ROTATION_0:
158                 t.setMatrix(sc, 1.f, 0.f, 0.f, 1.f);
159                 break;
160             case ROTATION_90:
161                 t.setMatrix(sc, 0.f, -1.f, 1.f, 0.f);
162                 break;
163             case ROTATION_180:
164                 t.setMatrix(sc, -1.f, 0.f, 0.f, -1.f);
165                 break;
166             case ROTATION_270:
167                 t.setMatrix(sc, 0.f, 1.f, -1.f, 0.f);
168                 break;
169         }
170     }
171 
172     /**
173      * Rotates a point CCW within a rectangle of size parentW x parentH with top/left at the
174      * origin as if the point is stuck to the rectangle. The rectangle is transformed such that
175      * it's top/left remains at the origin after the rotation.
176      */
rotatePoint(Point inOutPoint, @Rotation int rotation, int parentW, int parentH)177     public static void rotatePoint(Point inOutPoint, @Rotation int rotation,
178             int parentW, int parentH) {
179         int origX = inOutPoint.x;
180         switch (rotation) {
181             case ROTATION_0:
182                 return;
183             case ROTATION_90:
184                 inOutPoint.x = inOutPoint.y;
185                 inOutPoint.y = parentW - origX;
186                 return;
187             case ROTATION_180:
188                 inOutPoint.x = parentW - inOutPoint.x;
189                 inOutPoint.y = parentH - inOutPoint.y;
190                 return;
191             case ROTATION_270:
192                 inOutPoint.x = parentH - inOutPoint.y;
193                 inOutPoint.y = origX;
194         }
195     }
196 
197     /**
198      * Same as {@link #rotatePoint}, but for float coordinates.
199      */
rotatePointF(PointF inOutPoint, @Rotation int rotation, float parentW, float parentH)200     public static void rotatePointF(PointF inOutPoint, @Rotation int rotation,
201             float parentW, float parentH) {
202         float origX = inOutPoint.x;
203         switch (rotation) {
204             case ROTATION_0:
205                 return;
206             case ROTATION_90:
207                 inOutPoint.x = inOutPoint.y;
208                 inOutPoint.y = parentW - origX;
209                 return;
210             case ROTATION_180:
211                 inOutPoint.x = parentW - inOutPoint.x;
212                 inOutPoint.y = parentH - inOutPoint.y;
213                 return;
214             case ROTATION_270:
215                 inOutPoint.x = parentH - inOutPoint.y;
216                 inOutPoint.y = origX;
217         }
218     }
219 
220     /**
221      * Sets a matrix such that given a rotation, it transforms physical display
222      * coordinates to that rotation's logical coordinates.
223      *
224      * @param rotation the rotation to which the matrix should transform
225      * @param out the matrix to be set
226      */
transformPhysicalToLogicalCoordinates(@otation int rotation, @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out)227     public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
228             @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
229         switch (rotation) {
230             case ROTATION_0:
231                 out.reset();
232                 break;
233             case ROTATION_90:
234                 out.setRotate(270);
235                 out.postTranslate(0, physicalWidth);
236                 break;
237             case ROTATION_180:
238                 out.setRotate(180);
239                 out.postTranslate(physicalWidth, physicalHeight);
240                 break;
241             case ROTATION_270:
242                 out.setRotate(90);
243                 out.postTranslate(physicalHeight, 0);
244                 break;
245             default:
246                 throw new IllegalArgumentException("Unknown rotation: " + rotation);
247         }
248     }
249 
250     /**
251      * Reverses the rotation direction around the Z axis. Note that this method assumes all
252      * rotations are relative to {@link Surface.ROTATION_0}.
253      *
254      * @param rotation the original rotation.
255      * @return the new rotation that should be applied.
256      */
257     @Surface.Rotation
reverseRotationDirectionAroundZAxis(@urface.Rotation int rotation)258     public static int reverseRotationDirectionAroundZAxis(@Surface.Rotation int rotation) {
259         // Flipping 270 and 90 has the same effect as changing the direction which rotation is
260         // applied.
261         if (rotation == Surface.ROTATION_90) {
262             rotation = Surface.ROTATION_270;
263         } else if (rotation == Surface.ROTATION_270) {
264             rotation = Surface.ROTATION_90;
265         }
266         return rotation;
267     }
268 }
269