• 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.Rotation;
31 import android.view.SurfaceControl;
32 
33 /**
34  * A class containing utility methods related to rotation.
35  *
36  * @hide
37  */
38 public class RotationUtils {
39 
40     /**
41      * Rotates an Insets according to the given rotation.
42      */
rotateInsets(Insets insets, @Rotation int rotation)43     public static Insets rotateInsets(Insets insets, @Rotation int rotation) {
44         if (insets == null || insets == Insets.NONE) {
45             return insets;
46         }
47         Insets rotated;
48         switch (rotation) {
49             case ROTATION_0:
50                 rotated = insets;
51                 break;
52             case ROTATION_90:
53                 rotated = Insets.of(
54                         insets.top,
55                         insets.right,
56                         insets.bottom,
57                         insets.left);
58                 break;
59             case ROTATION_180:
60                 rotated = Insets.of(
61                         insets.right,
62                         insets.bottom,
63                         insets.left,
64                         insets.top);
65                 break;
66             case ROTATION_270:
67                 rotated = Insets.of(
68                         insets.bottom,
69                         insets.left,
70                         insets.top,
71                         insets.right);
72                 break;
73             default:
74                 throw new IllegalArgumentException("unknown rotation: " + rotation);
75         }
76         return rotated;
77     }
78 
79     /**
80      * Rotates bounds as if parentBounds and bounds are a group. The group is rotated from
81      * oldRotation to newRotation. This assumes that parentBounds is at 0,0 and remains at 0,0 after
82      * rotation. The bounds will be at the same physical position in parentBounds.
83      *
84      * Only 'inOutBounds' is mutated.
85      */
rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation, @Rotation int newRotation)86     public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int oldRotation,
87             @Rotation int newRotation) {
88         rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation));
89     }
90 
91     /**
92      * Rotates inOutBounds together with the parent for a given rotation delta. This assumes that
93      * the parent starts at 0,0 and remains at 0,0 after the rotation. The inOutBounds will remain
94      * at the same physical position within the parent.
95      *
96      * Only 'inOutBounds' is mutated.
97      */
rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight, @Rotation int rotation)98     public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
99             @Rotation int rotation) {
100         final int origLeft = inOutBounds.left;
101         final int origTop = inOutBounds.top;
102         switch (rotation) {
103             case ROTATION_0:
104                 return;
105             case ROTATION_90:
106                 inOutBounds.left = inOutBounds.top;
107                 inOutBounds.top = parentWidth - inOutBounds.right;
108                 inOutBounds.right = inOutBounds.bottom;
109                 inOutBounds.bottom = parentWidth - origLeft;
110                 return;
111             case ROTATION_180:
112                 inOutBounds.left = parentWidth - inOutBounds.right;
113                 inOutBounds.right = parentWidth - origLeft;
114                 inOutBounds.top = parentHeight - inOutBounds.bottom;
115                 inOutBounds.bottom = parentHeight - origTop;
116                 return;
117             case ROTATION_270:
118                 inOutBounds.left = parentHeight - inOutBounds.bottom;
119                 inOutBounds.bottom = inOutBounds.right;
120                 inOutBounds.right = parentHeight - inOutBounds.top;
121                 inOutBounds.top = origLeft;
122         }
123     }
124 
125     /**
126      * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
127      * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
128      * remains at 0,0 after rotation. The bounds will be at the same physical position in
129      * parentBounds.
130      *
131      * Only 'inOutBounds' is mutated.
132      */
rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation)133     public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
134         rotateBounds(inOutBounds, parentBounds.right, parentBounds.bottom, rotation);
135     }
136 
137     /** @return the rotation needed to rotate from oldRotation to newRotation. */
138     @Rotation
deltaRotation(@otation int oldRotation, @Rotation int newRotation)139     public static int deltaRotation(@Rotation int oldRotation, @Rotation int newRotation) {
140         int delta = newRotation - oldRotation;
141         if (delta < 0) delta += 4;
142         return delta;
143     }
144 
145     /**
146      * Rotates a surface CCW around the origin (eg. a 90-degree rotation will result in the
147      * bottom-left being at the origin). Use {@link #rotatePoint} to transform the top-left
148      * corner appropriately.
149      */
rotateSurface(SurfaceControl.Transaction t, SurfaceControl sc, @Rotation int rotation)150     public static void rotateSurface(SurfaceControl.Transaction t, SurfaceControl sc,
151             @Rotation int rotation) {
152         // Note: the matrix values look inverted, but they aren't because our coordinate-space
153         // is actually left-handed.
154         // Note: setMatrix expects values in column-major order.
155         switch (rotation) {
156             case ROTATION_0:
157                 t.setMatrix(sc, 1.f, 0.f, 0.f, 1.f);
158                 break;
159             case ROTATION_90:
160                 t.setMatrix(sc, 0.f, -1.f, 1.f, 0.f);
161                 break;
162             case ROTATION_180:
163                 t.setMatrix(sc, -1.f, 0.f, 0.f, -1.f);
164                 break;
165             case ROTATION_270:
166                 t.setMatrix(sc, 0.f, 1.f, -1.f, 0.f);
167                 break;
168         }
169     }
170 
171     /**
172      * Rotates a point CCW within a rectangle of size parentW x parentH with top/left at the
173      * origin as if the point is stuck to the rectangle. The rectangle is transformed such that
174      * it's top/left remains at the origin after the rotation.
175      */
rotatePoint(Point inOutPoint, @Rotation int rotation, int parentW, int parentH)176     public static void rotatePoint(Point inOutPoint, @Rotation int rotation,
177             int parentW, int parentH) {
178         int origX = inOutPoint.x;
179         switch (rotation) {
180             case ROTATION_0:
181                 return;
182             case ROTATION_90:
183                 inOutPoint.x = inOutPoint.y;
184                 inOutPoint.y = parentW - origX;
185                 return;
186             case ROTATION_180:
187                 inOutPoint.x = parentW - inOutPoint.x;
188                 inOutPoint.y = parentH - inOutPoint.y;
189                 return;
190             case ROTATION_270:
191                 inOutPoint.x = parentH - inOutPoint.y;
192                 inOutPoint.y = origX;
193         }
194     }
195 
196     /**
197      * Same as {@link #rotatePoint}, but for float coordinates.
198      */
rotatePointF(PointF inOutPoint, @Rotation int rotation, float parentW, float parentH)199     public static void rotatePointF(PointF inOutPoint, @Rotation int rotation,
200             float parentW, float parentH) {
201         float origX = inOutPoint.x;
202         switch (rotation) {
203             case ROTATION_0:
204                 return;
205             case ROTATION_90:
206                 inOutPoint.x = inOutPoint.y;
207                 inOutPoint.y = parentW - origX;
208                 return;
209             case ROTATION_180:
210                 inOutPoint.x = parentW - inOutPoint.x;
211                 inOutPoint.y = parentH - inOutPoint.y;
212                 return;
213             case ROTATION_270:
214                 inOutPoint.x = parentH - inOutPoint.y;
215                 inOutPoint.y = origX;
216         }
217     }
218 
219     /**
220      * Sets a matrix such that given a rotation, it transforms physical display
221      * coordinates to that rotation's logical coordinates.
222      *
223      * @param rotation the rotation to which the matrix should transform
224      * @param out the matrix to be set
225      */
transformPhysicalToLogicalCoordinates(@otation int rotation, @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out)226     public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
227             @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
228         switch (rotation) {
229             case ROTATION_0:
230                 out.reset();
231                 break;
232             case ROTATION_90:
233                 out.setRotate(270);
234                 out.postTranslate(0, physicalWidth);
235                 break;
236             case ROTATION_180:
237                 out.setRotate(180);
238                 out.postTranslate(physicalWidth, physicalHeight);
239                 break;
240             case ROTATION_270:
241                 out.setRotate(90);
242                 out.postTranslate(physicalHeight, 0);
243                 break;
244             default:
245                 throw new IllegalArgumentException("Unknown rotation: " + rotation);
246         }
247     }
248 }
249