1 /*
2  * Copyright (C) 2018 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 com.android.server.wm.utils;
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.annotation.Nullable;
26 import android.graphics.Matrix;
27 import android.graphics.Rect;
28 import android.graphics.RectF;
29 import android.view.DisplayInfo;
30 import android.view.Surface.Rotation;
31 
32 public class CoordinateTransforms {
33 
CoordinateTransforms()34     private CoordinateTransforms() {
35     }
36 
37     /**
38      * Sets a matrix such that given a rotation, it transforms physical display
39      * coordinates to that rotation's logical coordinates.
40      *
41      * @param rotation the rotation to which the matrix should transform
42      * @param out      the matrix to be set
43      */
transformPhysicalToLogicalCoordinates(@otation int rotation, @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out)44     public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
45             @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
46         switch (rotation) {
47             case ROTATION_0:
48                 out.reset();
49                 break;
50             case ROTATION_90:
51                 out.setRotate(270);
52                 out.postTranslate(0, physicalWidth);
53                 break;
54             case ROTATION_180:
55                 out.setRotate(180);
56                 out.postTranslate(physicalWidth, physicalHeight);
57                 break;
58             case ROTATION_270:
59                 out.setRotate(90);
60                 out.postTranslate(physicalHeight, 0);
61                 break;
62             default:
63                 throw new IllegalArgumentException("Unknown rotation: " + rotation);
64         }
65     }
66 
67     /**
68      * Sets a matrix such that given a rotation, it transforms that rotation's logical coordinates
69      * to physical coordinates.
70      *
71      * @param rotation the rotation to which the matrix should transform
72      * @param out      the matrix to be set
73      */
transformLogicalToPhysicalCoordinates(@otation int rotation, @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out)74     public static void transformLogicalToPhysicalCoordinates(@Rotation int rotation,
75             @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
76         switch (rotation) {
77             case ROTATION_0:
78                 out.reset();
79                 break;
80             case ROTATION_90:
81                 out.setRotate(90);
82                 out.preTranslate(0, -physicalWidth);
83                 break;
84             case ROTATION_180:
85                 out.setRotate(180);
86                 out.preTranslate(-physicalWidth, -physicalHeight);
87                 break;
88             case ROTATION_270:
89                 out.setRotate(270);
90                 out.preTranslate(-physicalHeight, 0);
91                 break;
92             default:
93                 throw new IllegalArgumentException("Unknown rotation: " + rotation);
94         }
95     }
96 
97     /**
98      * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
99      * old rotation to coordinates that refer to the same physical location in the new rotation.
100      *
101      * @param oldRotation the rotation to transform from
102      * @param newRotation the rotation to transform to
103      * @param info the display info
104      * @param out a matrix that will be set to the transform
105      */
transformToRotation(@otation int oldRotation, @Rotation int newRotation, DisplayInfo info, Matrix out)106     public static void transformToRotation(@Rotation int oldRotation,
107             @Rotation int newRotation, DisplayInfo info, Matrix out) {
108         final boolean flipped = info.rotation == ROTATION_90 || info.rotation == ROTATION_270;
109         final int h = flipped ? info.logicalWidth : info.logicalHeight;
110         final int w = flipped ? info.logicalHeight : info.logicalWidth;
111 
112         final Matrix tmp = new Matrix();
113         transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
114         transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
115         out.postConcat(tmp);
116     }
117 
118     /**
119      * Sets a matrix such that given a two rotations, that it transforms coordinates given in the
120      * old rotation to coordinates that refer to the same physical location in the new rotation.
121      *
122      * @param oldRotation the rotation to transform from
123      * @param newRotation the rotation to transform to
124      * @param newWidth the width of the area to transform, in the new rotation
125      * @param newHeight the height of the area to transform, in the new rotation
126      * @param out a matrix that will be set to the transform
127      */
transformToRotation(@otation int oldRotation, @Rotation int newRotation, int newWidth, int newHeight, Matrix out)128     public static void transformToRotation(@Rotation int oldRotation,
129             @Rotation int newRotation, int newWidth, int newHeight, Matrix out) {
130         final boolean flipped = newRotation == ROTATION_90 || newRotation == ROTATION_270;
131         final int h = flipped ? newWidth : newHeight;
132         final int w = flipped ? newHeight : newWidth;
133 
134         final Matrix tmp = new Matrix();
135         transformLogicalToPhysicalCoordinates(oldRotation, w, h, out);
136         transformPhysicalToLogicalCoordinates(newRotation, w, h, tmp);
137         out.postConcat(tmp);
138     }
139 
140     /**
141      * Transforms a rect using a transformation matrix
142      *
143      * @param transform the transformation to apply to the rect
144      * @param inOutRect the rect to transform
145      * @param tmp a temporary value, if null the function will allocate its own.
146      */
transformRect(Matrix transform, Rect inOutRect, @Nullable RectF tmp)147     public static void transformRect(Matrix transform, Rect inOutRect, @Nullable RectF tmp) {
148         if (tmp == null) {
149             tmp = new RectF();
150         }
151         tmp.set(inOutRect);
152         transform.mapRect(tmp);
153         inOutRect.set((int) tmp.left, (int) tmp.top, (int) tmp.right, (int) tmp.bottom);
154     }
155 }
156