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