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 static com.android.server.wm.utils.CoordinateTransforms.computeRotationMatrix;
25 import static com.android.server.wm.utils.CoordinateTransforms.transformLogicalToPhysicalCoordinates;
26 import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
27 import static com.android.server.wm.utils.CoordinateTransforms.transformToRotation;
28 
29 import static org.hamcrest.Matchers.is;
30 import static org.junit.Assert.*;
31 
32 import android.graphics.Matrix;
33 import android.graphics.Point;
34 import android.graphics.PointF;
35 import android.platform.test.annotations.Presubmit;
36 import android.view.DisplayInfo;
37 
38 import org.junit.Before;
39 import org.junit.Rule;
40 import org.junit.Test;
41 import org.junit.rules.ErrorCollector;
42 
43 /**
44  * Build/Install/Run:
45  *  atest WmTests:CoordinateTransformsTest
46  */
47 @Presubmit
48 public class CoordinateTransformsTest {
49 
50     private static final int W = 200;
51     private static final int H = 400;
52 
53     private final Matrix mMatrix = new Matrix();
54     private final Matrix mMatrix2 = new Matrix();
55 
56     @Rule
57     public final ErrorCollector mErrorCollector = new ErrorCollector();
58 
59     @Before
setUp()60     public void setUp() throws Exception {
61         mMatrix.setTranslate(0xdeadbeef, 0xdeadbeef);
62         mMatrix2.setTranslate(0xbeefdead, 0xbeefdead);
63     }
64 
65     @Test
transformPhysicalToLogicalCoordinates_rot0()66     public void transformPhysicalToLogicalCoordinates_rot0() {
67         transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix);
68         assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
69     }
70 
71     @Test
transformPhysicalToLogicalCoordinates_rot90()72     public void transformPhysicalToLogicalCoordinates_rot90() {
73         transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix);
74 
75         checkPoint(0, 0).transformsTo(0, W);
76         checkPoint(W, H).transformsTo(H, 0);
77     }
78 
79     @Test
transformPhysicalToLogicalCoordinates_rot180()80     public void transformPhysicalToLogicalCoordinates_rot180() {
81         transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix);
82 
83         checkPoint(0, 0).transformsTo(W, H);
84         checkPoint(W, H).transformsTo(0, 0);
85     }
86 
87     @Test
transformPhysicalToLogicalCoordinates_rot270()88     public void transformPhysicalToLogicalCoordinates_rot270() {
89         transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix);
90 
91         checkPoint(0, 0).transformsTo(H, 0);
92         checkPoint(W, H).transformsTo(0, W);
93     }
94 
95     @Test
transformLogicalToPhysicalCoordinates_rot0()96     public void transformLogicalToPhysicalCoordinates_rot0() {
97         transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
98         assertThat(mMatrix, is(Matrix.IDENTITY_MATRIX));
99     }
100 
101     @Test
transformLogicalToPhysicalCoordinates_rot90()102     public void transformLogicalToPhysicalCoordinates_rot90() {
103         transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
104 
105         checkPoint(0, W).transformsTo(0, 0);
106         checkPoint(H, 0).transformsTo(W, H);
107     }
108 
109     @Test
transformLogicalToPhysicalCoordinates_rot180()110     public void transformLogicalToPhysicalCoordinates_rot180() {
111         transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
112 
113         checkPoint(W, H).transformsTo(0, 0);
114         checkPoint(0, 0).transformsTo(W, H);
115     }
116 
117     @Test
transformLogicalToPhysicalCoordinates_rot270()118     public void transformLogicalToPhysicalCoordinates_rot270() {
119         transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
120 
121         checkPoint(H, 0).transformsTo(0, 0);
122         checkPoint(0, W).transformsTo(W, H);
123     }
124 
125     @Test
transformLogicalToPhysicalCoordinatesIsInverse_rot0()126     public void transformLogicalToPhysicalCoordinatesIsInverse_rot0() {
127         transformLogicalToPhysicalCoordinates(ROTATION_0, W, H, mMatrix);
128         transformPhysicalToLogicalCoordinates(ROTATION_0, W, H, mMatrix2);
129 
130         assertMatricesAreInverses(mMatrix, mMatrix2);
131     }
132 
133     @Test
transformLogicalToPhysicalCoordinatesIsInverse_rot90()134     public void transformLogicalToPhysicalCoordinatesIsInverse_rot90() {
135         transformLogicalToPhysicalCoordinates(ROTATION_90, W, H, mMatrix);
136         transformPhysicalToLogicalCoordinates(ROTATION_90, W, H, mMatrix2);
137 
138         assertMatricesAreInverses(mMatrix, mMatrix2);
139     }
140 
141     @Test
transformLogicalToPhysicalCoordinatesIsInverse_rot180()142     public void transformLogicalToPhysicalCoordinatesIsInverse_rot180() {
143         transformLogicalToPhysicalCoordinates(ROTATION_180, W, H, mMatrix);
144         transformPhysicalToLogicalCoordinates(ROTATION_180, W, H, mMatrix2);
145 
146         assertMatricesAreInverses(mMatrix, mMatrix2);
147     }
148 
149     @Test
transformLogicalToPhysicalCoordinatesIsInverse_rot270()150     public void transformLogicalToPhysicalCoordinatesIsInverse_rot270() {
151         transformLogicalToPhysicalCoordinates(ROTATION_270, W, H, mMatrix);
152         transformPhysicalToLogicalCoordinates(ROTATION_270, W, H, mMatrix2);
153 
154         assertMatricesAreInverses(mMatrix, mMatrix2);
155     }
156 
157     @Test
transformBetweenRotations_rot180_rot270()158     public void transformBetweenRotations_rot180_rot270() {
159         // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
160         transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix);
161 
162         checkPoint(0, 0).transformsTo(0, W);
163         checkPoint(W, H).transformsTo(H, 0);
164     }
165 
166     @Test
transformBetweenRotations_rot90_rot0()167     public void transformBetweenRotations_rot90_rot0() {
168         transformToRotation(ROTATION_180, ROTATION_270, W, H, mMatrix);
169 
170         checkPoint(0, 0).transformsTo(0, H);
171         // H,W is bottom right in ROT_90
172         checkPoint(H, W).transformsTo(W, 0);
173     }
174 
175     @Test
transformBetweenRotations_displayInfo()176     public void transformBetweenRotations_displayInfo() {
177         final DisplayInfo di = new DisplayInfo();
178         di.rotation = ROTATION_90;
179         di.logicalWidth = H;  // dimensions are flipped in ROT_90
180         di.logicalHeight = W;
181         transformToRotation(ROTATION_180, ROTATION_270, di, mMatrix);
182 
183         // W,H are flipped, because they need to be given in the new orientation, i.e. ROT_270.
184         transformToRotation(ROTATION_180, ROTATION_270, H, W, mMatrix2);
185 
186         assertEquals(mMatrix2, mMatrix);
187     }
188 
189     @Test
rotate_0_bottomRight()190     public void rotate_0_bottomRight() {
191         computeRotationMatrix(ROTATION_0, W, H, mMatrix);
192         PointF newPoints = checkMappedPoints(W, H);
193         assertEquals(W, newPoints.x, 0);
194         assertEquals(H, newPoints.y, 0);
195     }
196 
197     @Test
rotate_90_bottomRight()198     public void rotate_90_bottomRight() {
199         computeRotationMatrix(ROTATION_90, W, H, mMatrix);
200         PointF newPoints = checkMappedPoints(W, H);
201         assertEquals(0, newPoints.x, 0);
202         assertEquals(W, newPoints.y, 0);
203     }
204 
205     @Test
rotate_180_bottomRight()206     public void rotate_180_bottomRight() {
207         computeRotationMatrix(ROTATION_180, W, H, mMatrix);
208         PointF newPoints = checkMappedPoints(W, H);
209         assertEquals(0, newPoints.x, 0);
210         assertEquals(0, newPoints.y, 0);
211     }
212 
213     @Test
rotate_270_bottomRight()214     public void rotate_270_bottomRight() {
215         computeRotationMatrix(ROTATION_270, W, H, mMatrix);
216         PointF newPoints = checkMappedPoints(W, H);
217         assertEquals(H, newPoints.x, 0);
218         assertEquals(0, newPoints.y, 0);
219     }
220 
checkMappedPoints(int x, int y)221     private PointF checkMappedPoints(int x, int y) {
222         final float[] fs = new float[] {x, y};
223         mMatrix.mapPoints(fs);
224         return new PointF(fs[0], fs[1]);
225     }
226 
assertMatricesAreInverses(Matrix matrix, Matrix matrix2)227     private void assertMatricesAreInverses(Matrix matrix, Matrix matrix2) {
228         final Matrix concat = new Matrix();
229         concat.setConcat(matrix, matrix2);
230         assertTrue("expected identity, but was: " + concat, concat.isIdentity());
231     }
232 
checkPoint(int x, int y)233     private TransformPointAssertable checkPoint(int x, int y) {
234         final Point devicePoint = new Point(x, y);
235         final float[] fs = new float[] {x, y};
236         mMatrix.mapPoints(fs);
237         final PointF transformedPoint = new PointF(fs[0], fs[1]);
238 
239         return (expectedX, expectedY) -> {
240             mErrorCollector.checkThat("t(" + devicePoint + ")",
241                     transformedPoint, is(new PointF(expectedX, expectedY)));
242         };
243     }
244 
245     public interface TransformPointAssertable {
transformsTo(int x, int y)246         void transformsTo(int x, int y);
247     }
248 }
249