Felka Chang | 489cf26 | 2019-12-26 13:36:23 +0800 | [diff] [blame^] | 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.content.Context; |
| 20 | import android.graphics.Matrix; |
| 21 | import android.graphics.Path; |
| 22 | import android.graphics.Rect; |
| 23 | import android.graphics.RectF; |
| 24 | import android.graphics.Region; |
| 25 | import android.perftests.utils.BenchmarkState; |
| 26 | import android.perftests.utils.PerfStatusReporter; |
| 27 | import android.text.TextUtils; |
| 28 | import android.util.DisplayMetrics; |
| 29 | import android.util.Log; |
| 30 | import android.util.PathParser; |
| 31 | |
| 32 | import androidx.test.filters.LargeTest; |
| 33 | import androidx.test.platform.app.InstrumentationRegistry; |
| 34 | import androidx.test.runner.AndroidJUnit4; |
| 35 | |
| 36 | import org.junit.Before; |
| 37 | import org.junit.Rule; |
| 38 | import org.junit.Test; |
| 39 | import org.junit.runner.RunWith; |
| 40 | |
| 41 | @RunWith(AndroidJUnit4.class) |
| 42 | @LargeTest |
| 43 | public class CutoutSpecificationBenchmark { |
| 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 PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); |
| 71 | |
| 72 | private Context mContext; |
| 73 | private DisplayMetrics mDisplayMetrics; |
| 74 | |
| 75 | /** |
| 76 | * Setup the necessary member field used by test methods. |
| 77 | */ |
| 78 | @Before |
| 79 | public void setUp() { |
| 80 | mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); |
| 81 | |
| 82 | mDisplayMetrics = new DisplayMetrics(); |
| 83 | mContext.getDisplay().getRealMetrics(mDisplayMetrics); |
| 84 | } |
| 85 | |
| 86 | |
| 87 | private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) { |
| 88 | final RectF rectF = new RectF(); |
| 89 | p.computeBounds(rectF, false /* unused */); |
| 90 | rectF.round(inoutRect); |
| 91 | inoutRegion.op(inoutRect, Region.Op.UNION); |
| 92 | } |
| 93 | |
| 94 | private static void oldMethodParsingSpec(String spec, int displayWidth, int displayHeight, |
| 95 | float density) { |
| 96 | Path p = null; |
| 97 | Rect boundTop = null; |
| 98 | Rect boundBottom = null; |
| 99 | Rect safeInset = new Rect(); |
| 100 | String bottomSpec = null; |
| 101 | if (!TextUtils.isEmpty(spec)) { |
| 102 | spec = spec.trim(); |
| 103 | final float offsetX; |
| 104 | if (spec.endsWith(RIGHT_MARKER)) { |
| 105 | offsetX = displayWidth; |
| 106 | spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim(); |
| 107 | } else if (spec.endsWith(LEFT_MARKER)) { |
| 108 | offsetX = 0; |
| 109 | spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim(); |
| 110 | } else { |
| 111 | offsetX = displayWidth / 2f; |
| 112 | } |
| 113 | final boolean inDp = spec.endsWith(DP_MARKER); |
| 114 | if (inDp) { |
| 115 | spec = spec.substring(0, spec.length() - DP_MARKER.length()); |
| 116 | } |
| 117 | |
| 118 | if (spec.contains(BOTTOM_MARKER)) { |
| 119 | String[] splits = spec.split(BOTTOM_MARKER, 2); |
| 120 | spec = splits[0].trim(); |
| 121 | bottomSpec = splits[1].trim(); |
| 122 | } |
| 123 | |
| 124 | final Matrix m = new Matrix(); |
| 125 | final Region r = Region.obtain(); |
| 126 | if (!spec.isEmpty()) { |
| 127 | try { |
| 128 | p = PathParser.createPathFromPathData(spec); |
| 129 | } catch (Throwable e) { |
| 130 | Log.wtf(TAG, "Could not inflate cutout: ", e); |
| 131 | } |
| 132 | |
| 133 | if (p != null) { |
| 134 | if (inDp) { |
| 135 | m.postScale(density, density); |
| 136 | } |
| 137 | m.postTranslate(offsetX, 0); |
| 138 | p.transform(m); |
| 139 | |
| 140 | boundTop = new Rect(); |
| 141 | toRectAndAddToRegion(p, r, boundTop); |
| 142 | safeInset.top = boundTop.bottom; |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | if (bottomSpec != null) { |
| 147 | int bottomInset = 0; |
| 148 | Path bottomPath = null; |
| 149 | try { |
| 150 | bottomPath = PathParser.createPathFromPathData(bottomSpec); |
| 151 | } catch (Throwable e) { |
| 152 | Log.wtf(TAG, "Could not inflate bottom cutout: ", e); |
| 153 | } |
| 154 | |
| 155 | if (bottomPath != null) { |
| 156 | // Keep top transform |
| 157 | m.postTranslate(0, displayHeight); |
| 158 | bottomPath.transform(m); |
| 159 | p.addPath(bottomPath); |
| 160 | boundBottom = new Rect(); |
| 161 | toRectAndAddToRegion(bottomPath, r, boundBottom); |
| 162 | bottomInset = displayHeight - boundBottom.top; |
| 163 | } |
| 164 | safeInset.bottom = bottomInset; |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | @Test |
| 170 | public void parseByOldMethodForDoubleCutout() { |
| 171 | final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); |
| 172 | while (state.keepRunning()) { |
| 173 | oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels, |
| 174 | mDisplayMetrics.heightPixels, mDisplayMetrics.density); |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | @Test |
| 179 | public void parseByNewMethodForDoubleCutout() { |
| 180 | final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); |
| 181 | while (state.keepRunning()) { |
| 182 | new CutoutSpecification.Parser(mDisplayMetrics.density, |
| 183 | mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels) |
| 184 | .parse(DOUBLE_CUTOUT_SPEC); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | @Test |
| 189 | public void parseLongEdgeCutout() { |
| 190 | final String spec = "M 0,0\n" |
| 191 | + "H 48\n" |
| 192 | + "V 48\n" |
| 193 | + "H -48\n" |
| 194 | + "Z\n" |
| 195 | + "@left\n" |
| 196 | + "@center_vertical\n" |
| 197 | + "M 0,0\n" |
| 198 | + "H 48\n" |
| 199 | + "V 48\n" |
| 200 | + "H -48\n" |
| 201 | + "Z\n" |
| 202 | + "@left\n" |
| 203 | + "@center_vertical\n" |
| 204 | + "M 0,0\n" |
| 205 | + "H -48\n" |
| 206 | + "V 48\n" |
| 207 | + "H 48\n" |
| 208 | + "Z\n" |
| 209 | + "@right\n" |
| 210 | + "@dp"; |
| 211 | |
| 212 | final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); |
| 213 | while (state.keepRunning()) { |
| 214 | new CutoutSpecification.Parser(mDisplayMetrics.density, |
| 215 | mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | @Test |
| 220 | public void parseShortEdgeCutout() { |
| 221 | final String spec = "M 0,0\n" |
| 222 | + "H 48\n" |
| 223 | + "V 48\n" |
| 224 | + "H -48\n" |
| 225 | + "Z\n" |
| 226 | + "@bottom\n" |
| 227 | + "M 0,0\n" |
| 228 | + "H 48\n" |
| 229 | + "V -48\n" |
| 230 | + "H -48\n" |
| 231 | + "Z\n" |
| 232 | + "@dp"; |
| 233 | |
| 234 | final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); |
| 235 | while (state.keepRunning()) { |
| 236 | new CutoutSpecification.Parser(mDisplayMetrics.density, |
| 237 | mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec); |
| 238 | } |
| 239 | } |
| 240 | } |