• 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.view;
18 
19 import android.graphics.Matrix;
20 import android.graphics.Path;
21 import android.graphics.Rect;
22 import android.graphics.RectF;
23 import android.graphics.Region;
24 import android.text.TextUtils;
25 import android.util.Log;
26 import android.util.PathParser;
27 
28 import androidx.benchmark.BenchmarkState;
29 import androidx.benchmark.junit4.BenchmarkRule;
30 import androidx.test.filters.LargeTest;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 
37 @RunWith(AndroidJUnit4.class)
38 @LargeTest
39 public class CutoutSpecificationBenchmark {
40     private static final int DISPLAY_WIDTH = 1080;
41     private static final int DISPLAY_HEIGHT = 1920;
42     private static final float DISPLAY_DENSITY = 3.5f;
43 
44     private static final String TAG = "CutoutSpecificationBenchmark";
45 
46     private static final String BOTTOM_MARKER = "@bottom";
47     private static final String DP_MARKER = "@dp";
48     private static final String RIGHT_MARKER = "@right";
49     private static final String LEFT_MARKER = "@left";
50 
51     private static final String DOUBLE_CUTOUT_SPEC = "M 0,0\n"
52             + "L -72, 0\n"
53             + "L -69.9940446283, 20.0595537175\n"
54             + "C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0\n"
55             + "L 56.8, 32.0\n"
56             + "C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175\n"
57             + "L 72, 0\n"
58             + "Z\n"
59             + "@bottom\n"
60             + "M 0,0\n"
61             + "L -72, 0\n"
62             + "L -69.9940446283, -20.0595537175\n"
63             + "C -69.1582133885, -28.4178661152 -65.2, -32.0 -56.8, -32.0\n"
64             + "L 56.8, -32.0\n"
65             + "C 65.2, -32.0 69.1582133885, -28.4178661152 69.9940446283, -20.0595537175\n"
66             + "L 72, 0\n"
67             + "Z\n"
68             + "@dp";
69     @Rule
70     public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
71 
toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect)72     private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
73         final RectF rectF = new RectF();
74         p.computeBounds(rectF, false /* unused */);
75         rectF.round(inoutRect);
76         inoutRegion.op(inoutRect, Region.Op.UNION);
77     }
78 
oldMethodParsingSpec(String spec, int displayWidth, int displayHeight, float density)79     private static void oldMethodParsingSpec(String spec, int displayWidth, int displayHeight,
80             float density) {
81         Path p = null;
82         Rect boundTop = null;
83         Rect boundBottom = null;
84         Rect safeInset = new Rect();
85         String bottomSpec = null;
86         if (!TextUtils.isEmpty(spec)) {
87             spec = spec.trim();
88             final float offsetX;
89             if (spec.endsWith(RIGHT_MARKER)) {
90                 offsetX = displayWidth;
91                 spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
92             } else if (spec.endsWith(LEFT_MARKER)) {
93                 offsetX = 0;
94                 spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
95             } else {
96                 offsetX = displayWidth / 2f;
97             }
98             final boolean inDp = spec.endsWith(DP_MARKER);
99             if (inDp) {
100                 spec = spec.substring(0, spec.length() - DP_MARKER.length());
101             }
102 
103             if (spec.contains(BOTTOM_MARKER)) {
104                 String[] splits = spec.split(BOTTOM_MARKER, 2);
105                 spec = splits[0].trim();
106                 bottomSpec = splits[1].trim();
107             }
108 
109             final Matrix m = new Matrix();
110             final Region r = Region.obtain();
111             if (!spec.isEmpty()) {
112                 try {
113                     p = PathParser.createPathFromPathData(spec);
114                 } catch (Throwable e) {
115                     Log.wtf(TAG, "Could not inflate cutout: ", e);
116                 }
117 
118                 if (p != null) {
119                     if (inDp) {
120                         m.postScale(density, density);
121                     }
122                     m.postTranslate(offsetX, 0);
123                     p.transform(m);
124 
125                     boundTop = new Rect();
126                     toRectAndAddToRegion(p, r, boundTop);
127                     safeInset.top = boundTop.bottom;
128                 }
129             }
130 
131             if (bottomSpec != null) {
132                 int bottomInset = 0;
133                 Path bottomPath = null;
134                 try {
135                     bottomPath = PathParser.createPathFromPathData(bottomSpec);
136                 } catch (Throwable e) {
137                     Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
138                 }
139 
140                 if (bottomPath != null) {
141                     // Keep top transform
142                     m.postTranslate(0, displayHeight);
143                     bottomPath.transform(m);
144                     p.addPath(bottomPath);
145                     boundBottom = new Rect();
146                     toRectAndAddToRegion(bottomPath, r, boundBottom);
147                     bottomInset = displayHeight - boundBottom.top;
148                 }
149                 safeInset.bottom = bottomInset;
150             }
151         }
152     }
153 
154     @Test
parseByOldMethodForDoubleCutout()155     public void parseByOldMethodForDoubleCutout() {
156         final BenchmarkState state = mBenchmarkRule.getState();
157         while (state.keepRunning()) {
158             oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
159                     DISPLAY_DENSITY);
160         }
161     }
162 
163     @Test
parseByNewMethodForDoubleCutout()164     public void parseByNewMethodForDoubleCutout() {
165         final BenchmarkState state = mBenchmarkRule.getState();
166         while (state.keepRunning()) {
167             new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
168                     .parse(DOUBLE_CUTOUT_SPEC);
169         }
170     }
171 
172     @Test
parseLongEdgeCutout()173     public void parseLongEdgeCutout() {
174         final String spec = "M 0,0\n"
175                 + "H 48\n"
176                 + "V 48\n"
177                 + "H -48\n"
178                 + "Z\n"
179                 + "@left\n"
180                 + "@center_vertical\n"
181                 + "M 0,0\n"
182                 + "H 48\n"
183                 + "V 48\n"
184                 + "H -48\n"
185                 + "Z\n"
186                 + "@left\n"
187                 + "@center_vertical\n"
188                 + "M 0,0\n"
189                 + "H -48\n"
190                 + "V 48\n"
191                 + "H 48\n"
192                 + "Z\n"
193                 + "@right\n"
194                 + "@dp";
195 
196         final BenchmarkState state = mBenchmarkRule.getState();
197         while (state.keepRunning()) {
198             new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
199                     .parse(spec);
200         }
201     }
202 
203     @Test
parseShortEdgeCutout()204     public void parseShortEdgeCutout() {
205         final String spec = "M 0,0\n"
206                 + "H 48\n"
207                 + "V 48\n"
208                 + "H -48\n"
209                 + "Z\n"
210                 + "@bottom\n"
211                 + "M 0,0\n"
212                 + "H 48\n"
213                 + "V -48\n"
214                 + "H -48\n"
215                 + "Z\n"
216                 + "@dp";
217 
218         final BenchmarkState state = mBenchmarkRule.getState();
219         while (state.keepRunning()) {
220             new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
221                     .parse(spec);
222         }
223     }
224 }
225