1 /* 2 * Copyright (C) 2015 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.uirendering.cts.testclasses; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.animation.ValueAnimator; 22 import android.content.pm.PackageManager; 23 import android.graphics.Color; 24 import android.graphics.ColorMatrix; 25 import android.graphics.ColorMatrixColorFilter; 26 import android.graphics.Matrix; 27 import android.graphics.Paint; 28 import android.graphics.Point; 29 import android.graphics.PorterDuff; 30 import android.graphics.PorterDuffXfermode; 31 import android.graphics.Rect; 32 import android.uirendering.cts.R; 33 import android.uirendering.cts.bitmapcomparers.MSSIMComparer; 34 import android.uirendering.cts.bitmapverifiers.ColorCountVerifier; 35 import android.uirendering.cts.bitmapverifiers.ColorVerifier; 36 import android.uirendering.cts.bitmapverifiers.RectVerifier; 37 import android.uirendering.cts.bitmapverifiers.SamplePointVerifier; 38 import android.uirendering.cts.testinfrastructure.ActivityTestBase; 39 import android.uirendering.cts.testinfrastructure.ViewInitializer; 40 import android.uirendering.cts.util.WebViewReadyHelper; 41 import android.view.Gravity; 42 import android.view.View; 43 import android.view.ViewTreeObserver; 44 import android.webkit.WebView; 45 import android.widget.FrameLayout; 46 47 import androidx.annotation.ColorInt; 48 import androidx.test.filters.LargeTest; 49 import androidx.test.filters.MediumTest; 50 import androidx.test.runner.AndroidJUnit4; 51 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 55 import java.util.concurrent.CountDownLatch; 56 57 @MediumTest 58 @RunWith(AndroidJUnit4.class) 59 public class LayerTests extends ActivityTestBase { 60 @Test testLayerPaintAlpha()61 public void testLayerPaintAlpha() { 62 // red channel full strength, other channels 75% strength 63 // (since 25% alpha red subtracts from them) 64 @ColorInt 65 final int expectedColor = Color.rgb(255, 191, 191); 66 createTest() 67 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 68 // reduce alpha by 50% 69 Paint paint = new Paint(); 70 paint.setAlpha(128); 71 view.setLayerType(View.LAYER_TYPE_HARDWARE, paint); 72 73 // reduce alpha by another 50% (ensuring two alphas combine correctly) 74 view.setAlpha(0.5f); 75 }) 76 .runWithVerifier(new ColorVerifier(expectedColor)); 77 } 78 79 @Test testLayerPaintSimpleAlphaWithHardware()80 public void testLayerPaintSimpleAlphaWithHardware() { 81 @ColorInt 82 final int expectedColor = Color.rgb(255, 128, 128); 83 createTest() 84 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 85 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 86 87 // reduce alpha, so that overdraw will result in a different color 88 view.setAlpha(0.5f); 89 }) 90 .runWithVerifier(new ColorVerifier(expectedColor)); 91 } 92 93 @Test testLayerPaintSimpleAlphaWithSoftware()94 public void testLayerPaintSimpleAlphaWithSoftware() { 95 @ColorInt 96 final int expectedColor = Color.rgb(255, 128, 128); 97 createTest() 98 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 99 view.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 100 101 // reduce alpha, so that overdraw will result in a different color 102 view.setAlpha(0.5f); 103 }) 104 .runWithVerifier(new ColorVerifier(expectedColor)); 105 } 106 107 @Test testLayerPaintXfermodeWithSoftware()108 public void testLayerPaintXfermodeWithSoftware() { 109 createTest() 110 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 111 Paint paint = new Paint(); 112 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 113 view.setLayerType(View.LAYER_TYPE_SOFTWARE, paint); 114 }, true) 115 .runWithVerifier(new ColorVerifier(Color.TRANSPARENT)); 116 } 117 118 @Test testLayerPaintAlphaChanged()119 public void testLayerPaintAlphaChanged() { 120 final CountDownLatch fence = new CountDownLatch(1); 121 createTest() 122 .addLayout(R.layout.frame_layout, view -> { 123 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 124 View child = new View(view.getContext()); 125 child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 126 child.setAlpha(0.0f); 127 // add rendering content 128 child.setBackgroundColor(Color.RED); 129 root.addView(child, new FrameLayout.LayoutParams(TEST_WIDTH, TEST_HEIGHT, 130 Gravity.TOP | Gravity.LEFT)); 131 132 // Post non-zero alpha a few frames in, so that the initial layer draw completes. 133 root.getViewTreeObserver().addOnPreDrawListener( 134 new ViewTreeObserver.OnPreDrawListener() { 135 int mDrawCount = 0; 136 @Override 137 public boolean onPreDraw() { 138 if (mDrawCount++ == 5) { 139 root.getChildAt(0).setAlpha(1.00f); 140 root.getViewTreeObserver().removeOnPreDrawListener(this); 141 root.post(fence::countDown); 142 } else { 143 root.postInvalidate(); 144 } 145 return true; 146 } 147 }); 148 }, true, fence) 149 .runWithVerifier(new ColorVerifier(Color.RED)); 150 } 151 152 @Test testLayerPaintColorFilter()153 public void testLayerPaintColorFilter() { 154 // Red, fully desaturated. Note that it's not 255/3 in each channel. 155 // See ColorMatrix#setSaturation() 156 @ColorInt 157 final int expectedColor = Color.rgb(54, 54, 54); 158 createTest() 159 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 160 Paint paint = new Paint(); 161 ColorMatrix desatMatrix = new ColorMatrix(); 162 desatMatrix.setSaturation(0.0f); 163 paint.setColorFilter(new ColorMatrixColorFilter(desatMatrix)); 164 view.setLayerType(View.LAYER_TYPE_HARDWARE, paint); 165 }) 166 .runWithVerifier(new ColorVerifier(expectedColor)); 167 } 168 169 @Test testLayerPaintBlend()170 public void testLayerPaintBlend() { 171 // Red, drawn underneath opaque white, so output should be white. 172 // TODO: consider doing more interesting blending test here 173 @ColorInt 174 final int expectedColor = Color.WHITE; 175 createTest() 176 .addLayout(R.layout.simple_red_layout, (ViewInitializer) view -> { 177 Paint paint = new Paint(); 178 /* Note that when drawing in SW, we're blending within an otherwise empty 179 * SW layer, as opposed to in the frame buffer (which has a white 180 * background). 181 * 182 * For this reason we use just use DST, which just throws out the SRC 183 * content, regardless of the DST alpha channel. 184 */ 185 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST)); 186 view.setLayerType(View.LAYER_TYPE_HARDWARE, paint); 187 }) 188 .runWithVerifier(new ColorVerifier(expectedColor)); 189 } 190 191 @LargeTest 192 @Test testLayerClear()193 public void testLayerClear() { 194 ViewInitializer initializer = new ViewInitializer() { 195 ValueAnimator mAnimator = ValueAnimator.ofInt(0, 20); 196 @Override 197 public void initializeView(View view) { 198 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 199 root.setAlpha(0.5f); 200 201 final View child = new View(view.getContext()); 202 child.setBackgroundColor(Color.BLUE); 203 child.setTranslationX(10); 204 child.setLayoutParams( 205 new FrameLayout.LayoutParams(50, 50)); 206 child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 207 root.addView(child); 208 209 mAnimator.setRepeatMode(ValueAnimator.REVERSE); 210 mAnimator.setRepeatCount(ValueAnimator.INFINITE); 211 mAnimator.setDuration(200); 212 mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 213 @Override 214 public void onAnimationUpdate(ValueAnimator animation) { 215 child.setTranslationY((Integer) mAnimator.getAnimatedValue()); 216 } 217 }); 218 mAnimator.start(); 219 } 220 @Override 221 public void teardownView() { 222 mAnimator.cancel(); 223 } 224 }; 225 226 createTest() 227 .addLayout(R.layout.frame_layout, initializer, true) 228 .runWithAnimationVerifier(new ColorCountVerifier( 229 Color.WHITE, 90 * 90 - 50 * 50, 10)); 230 } 231 232 @Test testAlphaLayerChild()233 public void testAlphaLayerChild() { 234 ViewInitializer initializer = new ViewInitializer() { 235 @Override 236 public void initializeView(View view) { 237 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 238 root.setAlpha(0.5f); 239 240 View child = new View(view.getContext()); 241 child.setBackgroundColor(Color.BLUE); 242 child.setTranslationX(10); 243 child.setTranslationY(10); 244 child.setLayoutParams( 245 new FrameLayout.LayoutParams(50, 50)); 246 child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 247 root.addView(child); 248 } 249 }; 250 251 createTest() 252 .addLayout(R.layout.frame_layout, initializer) 253 .runWithVerifier(new RectVerifier(Color.WHITE, 0xff8080ff, 254 new Rect(10, 10, 60, 60))); 255 } 256 257 @Test testLayerInitialSizeZero()258 public void testLayerInitialSizeZero() { 259 createTest() 260 .addLayout(R.layout.frame_layout, view -> { 261 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 262 // disable clipChildren, to ensure children aren't rejected by bounds 263 root.setClipChildren(false); 264 for (int i = 0; i < 2; i++) { 265 View child = new View(view.getContext()); 266 child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 267 // add rendering content, so View isn't skipped at render time 268 child.setBackgroundColor(Color.RED); 269 270 // add one with width=0, one with height=0 271 root.addView(child, new FrameLayout.LayoutParams( 272 i == 0 ? 0 : 90, 273 i == 0 ? 90 : 0, 274 Gravity.TOP | Gravity.LEFT)); 275 } 276 }, true) 277 .runWithVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */)); 278 } 279 280 @Test testLayerResizeZero()281 public void testLayerResizeZero() { 282 final CountDownLatch fence = new CountDownLatch(1); 283 createTest() 284 .addLayout(R.layout.frame_layout, view -> { 285 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 286 // disable clipChildren, to ensure child isn't rejected by bounds 287 root.setClipChildren(false); 288 for (int i = 0; i < 2; i++) { 289 View child = new View(view.getContext()); 290 child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 291 // add rendering content, so View isn't skipped at render time 292 child.setBackgroundColor(Color.BLUE); 293 root.addView(child, new FrameLayout.LayoutParams(90, 90, 294 Gravity.TOP | Gravity.LEFT)); 295 } 296 297 // post invalid dimensions a few frames in, so initial layer allocation succeeds 298 // NOTE: this must execute before capture, or verification will fail 299 root.getViewTreeObserver().addOnPreDrawListener( 300 new ViewTreeObserver.OnPreDrawListener() { 301 int mDrawCount = 0; 302 @Override 303 public boolean onPreDraw() { 304 if (mDrawCount++ == 5) { 305 root.getChildAt(0).getLayoutParams().width = 0; 306 root.getChildAt(0).requestLayout(); 307 root.getChildAt(1).getLayoutParams().height = 0; 308 root.getChildAt(1).requestLayout(); 309 root.getViewTreeObserver().removeOnPreDrawListener(this); 310 root.post(fence::countDown); 311 } else { 312 root.postInvalidate(); 313 } 314 return true; 315 } 316 }); 317 }, true, fence) 318 .runWithVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */)); 319 } 320 321 @Test testSaveLayerWithColorFilter()322 public void testSaveLayerWithColorFilter() { 323 // verify that renderer can draw nested clipped layers with chained color filters 324 createTest() 325 .addCanvasClient((canvas, width, height) -> { 326 Paint redPaint = new Paint(); 327 redPaint.setColor(0xffff0000); 328 Paint firstLayerPaint = new Paint(); 329 float[] blueToGreenMatrix = new float[20]; 330 blueToGreenMatrix[7] = blueToGreenMatrix[18] = 1.0f; 331 ColorMatrixColorFilter blueToGreenFilter = new ColorMatrixColorFilter(blueToGreenMatrix); 332 firstLayerPaint.setColorFilter(blueToGreenFilter); 333 Paint secondLayerPaint = new Paint(); 334 float[] redToBlueMatrix = new float[20]; 335 redToBlueMatrix[10] = redToBlueMatrix[18] = 1.0f; 336 ColorMatrixColorFilter redToBlueFilter = new ColorMatrixColorFilter(redToBlueMatrix); 337 secondLayerPaint.setColorFilter(redToBlueFilter); 338 // The color filters are applied starting first with the inner layer and then the 339 // outer layer. 340 canvas.saveLayer(40, 5, 80, 70, firstLayerPaint); 341 canvas.saveLayer(5, 40, 70, 80, secondLayerPaint); 342 canvas.drawRect(10, 10, 70, 70, redPaint); 343 canvas.restore(); 344 canvas.restore(); 345 }) 346 .runWithVerifier(new RectVerifier(Color.WHITE, Color.GREEN, new Rect(40, 40, 70, 70))); 347 } 348 349 @Test testSaveLayerWithAlpha()350 public void testSaveLayerWithAlpha() { 351 // verify that renderer can draw nested clipped layers with different alpha 352 createTest() // picture mode is disable due to bug:34871089 353 .addCanvasClient((canvas, width, height) -> { 354 Paint redPaint = new Paint(); 355 redPaint.setColor(0xffff0000); 356 canvas.saveLayerAlpha(40, 5, 80, 70, 0x7f); 357 canvas.saveLayerAlpha(5, 40, 70, 80, 0x3f); 358 canvas.drawRect(10, 10, 70, 70, redPaint); 359 canvas.restore(); 360 canvas.restore(); 361 }) 362 .runWithVerifier(new RectVerifier(Color.WHITE, 0xffffE0E0, new Rect(40, 40, 70, 70))); 363 } 364 365 @Test testSaveLayerRestoreBehavior()366 public void testSaveLayerRestoreBehavior() { 367 createTest() 368 .addCanvasClient((canvas, width, height) -> { 369 //set identity matrix 370 Matrix identity = new Matrix(); 371 canvas.setMatrix(identity); 372 final Paint p = new Paint(); 373 374 canvas.saveLayer(0, 0, width, height, p); 375 376 //change matrix and clip to something different 377 canvas.clipRect(0, 0, width >> 1, height >> 1); 378 Matrix scaledMatrix = new Matrix(); 379 scaledMatrix.setScale(4, 5); 380 canvas.setMatrix(scaledMatrix); 381 assertEquals(scaledMatrix, canvas.getMatrix()); 382 383 canvas.drawColor(Color.BLUE); 384 canvas.restore(); 385 386 //check if identity matrix is restored 387 assertEquals(identity, canvas.getMatrix()); 388 389 //should draw to the entire canvas, because clip has been removed 390 canvas.drawColor(Color.RED); 391 }) 392 .runWithVerifier(new ColorVerifier(Color.RED)); 393 } 394 395 @LargeTest 396 @Test testWebViewWithLayer()397 public void testWebViewWithLayer() { 398 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 399 return; // no WebView to run test on 400 } 401 CountDownLatch hwFence = new CountDownLatch(1); 402 createTest() 403 .addLayout(R.layout.test_content_webview, (ViewInitializer) view -> { 404 WebView webview = view.requireViewById(R.id.webview); 405 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 406 helper.loadData("<body style=\"background-color:blue\">"); 407 webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); 408 }, true, hwFence) 409 .runWithVerifier(new ColorVerifier(Color.BLUE)); 410 } 411 412 @LargeTest 413 @Test testWebViewWithOffsetLayer()414 public void testWebViewWithOffsetLayer() { 415 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 416 return; // no WebView to run test on 417 } 418 CountDownLatch hwFence = new CountDownLatch(1); 419 createTest() 420 .addLayout(R.layout.frame_layout_webview, (ViewInitializer) view -> { 421 FrameLayout layout = view.requireViewById(R.id.frame_layout); 422 layout.setBackgroundColor(Color.RED); 423 424 WebView webview = view.requireViewById(R.id.webview); 425 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 426 helper.loadData("<body style=\"background-color:blue\">"); 427 428 webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); 429 webview.setTranslationX(10); 430 webview.setTranslationY(10); 431 webview.getLayoutParams().width = TEST_WIDTH - 20; 432 webview.getLayoutParams().height = TEST_HEIGHT - 20; 433 }, true, hwFence) 434 .runWithVerifier(new RectVerifier(Color.RED, Color.BLUE, 435 new Rect(10, 10, TEST_WIDTH - 10, TEST_HEIGHT - 10))); 436 } 437 438 @LargeTest 439 @Test testWebViewNoOverlappingRenderingAndAlpha()440 public void testWebViewNoOverlappingRenderingAndAlpha() { 441 // Turn off overlapping rendering and apply an alpha. The current behavior is 442 // technically wrong - the alpha is ignored. But the only straightforward way to respect it 443 // would be to promote to a layer, which defeats the purpose of using 444 // forceHasOverlappingRendering, which is to skip promoting to a layer for speed. 445 // If we do ever respect the alpha, the verifier will need to be updated. In the meantime, 446 // this test verifies that we do not crash. 447 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 448 return; // no WebView to run test on 449 } 450 CountDownLatch hwFence = new CountDownLatch(1); 451 createTest() 452 .addLayout(R.layout.frame_layout_webview, (ViewInitializer) view -> { 453 FrameLayout layout = view.requireViewById(R.id.frame_layout); 454 layout.forceHasOverlappingRendering(false); 455 layout.setAlpha(.5f); 456 457 WebView webview = view.requireViewById(R.id.webview); 458 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 459 helper.loadData("<body style=\"background-color:blue\">"); 460 }, true, hwFence) 461 // See comments above. This verifies the current behavior, but more importantly, 462 // verifies that this does not crash. 463 .runWithVerifier(new ColorVerifier(Color.BLUE)); 464 } 465 466 @LargeTest 467 @Test testWebViewWithParentLayer()468 public void testWebViewWithParentLayer() { 469 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 470 return; // no WebView to run test on 471 } 472 CountDownLatch hwFence = new CountDownLatch(1); 473 createTest() 474 .addLayout(R.layout.frame_layout_webview, (ViewInitializer) view -> { 475 FrameLayout layout = view.requireViewById(R.id.frame_layout); 476 layout.setBackgroundColor(Color.RED); 477 layout.setLayerType(View.LAYER_TYPE_HARDWARE, null); 478 479 WebView webview = view.requireViewById(R.id.webview); 480 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 481 helper.loadData("<body style=\"background-color:blue\">"); 482 483 webview.setTranslationX(10); 484 webview.setTranslationY(10); 485 webview.getLayoutParams().width = TEST_WIDTH - 20; 486 webview.getLayoutParams().height = TEST_HEIGHT - 20; 487 488 }, true, hwFence) 489 .runWithVerifier(new RectVerifier(Color.RED, Color.BLUE, 490 new Rect(10, 10, TEST_WIDTH - 10, TEST_HEIGHT - 10))); 491 } 492 493 @LargeTest 494 @Test testWebViewScaledWithParentLayer()495 public void testWebViewScaledWithParentLayer() { 496 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 497 return; // no WebView to run test on 498 } 499 CountDownLatch hwFence = new CountDownLatch(1); 500 createTest() 501 .addLayout(R.layout.frame_layout_webview, (ViewInitializer) view -> { 502 FrameLayout layout = view.requireViewById(R.id.frame_layout); 503 layout.setBackgroundColor(Color.RED); 504 layout.setLayerType(View.LAYER_TYPE_HARDWARE, null); 505 506 WebView webview = view.requireViewById(R.id.webview); 507 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 508 helper.loadData("<body style=\"background-color:blue\">"); 509 510 webview.setTranslationX(10); 511 webview.setTranslationY(10); 512 webview.setScaleX(0.5f); 513 webview.getLayoutParams().width = 40; 514 webview.getLayoutParams().height = 40; 515 516 }, true, hwFence) 517 .runWithVerifier(new RectVerifier(Color.RED, Color.BLUE, 518 new Rect(20, 10, 40, 50))); 519 } 520 521 @LargeTest 522 @Test testWebViewWithAlpha()523 public void testWebViewWithAlpha() { 524 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 525 return; // no WebView to run test on 526 } 527 CountDownLatch hwFence = new CountDownLatch(1); 528 createTest() 529 .addLayout(R.layout.test_content_webview, (ViewInitializer) view -> { 530 WebView webview = view.requireViewById(R.id.webview); 531 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 532 helper.loadData("<body style=\"background-color:blue\">"); 533 534 // reduce alpha by 50% 535 webview.setAlpha(0.5f); 536 537 }, true, hwFence) 538 .runWithVerifier(new ColorVerifier(Color.rgb(128, 128, 255))); 539 } 540 541 @LargeTest 542 @Test testWebViewWithAlphaLayer()543 public void testWebViewWithAlphaLayer() { 544 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 545 return; // no WebView to run test on 546 } 547 CountDownLatch hwFence = new CountDownLatch(1); 548 createTest() 549 .addLayout(R.layout.test_content_webview, (ViewInitializer) view -> { 550 WebView webview = view.requireViewById(R.id.webview); 551 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 552 helper.loadData("<body style=\"background-color:blue\">"); 553 554 // reduce alpha by 50% 555 Paint paint = new Paint(); 556 paint.setAlpha(128); 557 webview.setLayerType(View.LAYER_TYPE_HARDWARE, paint); 558 559 // reduce alpha by another 50% (ensuring two alphas combine correctly) 560 webview.setAlpha(0.5f); 561 562 }, true, hwFence) 563 .runWithVerifier(new ColorVerifier(Color.rgb(191, 191, 255))); 564 } 565 566 @LargeTest 567 @Test testWebViewWithUnclippedLayer()568 public void testWebViewWithUnclippedLayer() { 569 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 570 return; // no WebView to run test on 571 } 572 CountDownLatch hwFence = new CountDownLatch(1); 573 Point[] testPoints = new Point[] { 574 // solid area 575 new Point(0, 0), 576 new Point(0, TEST_HEIGHT - 1), 577 // fade area 578 new Point(0, TEST_HEIGHT - 10), 579 new Point(0, TEST_HEIGHT - 5) 580 }; 581 createTest() 582 .addLayout(R.layout.test_content_webview, (ViewInitializer) view -> { 583 WebView webview = view.requireViewById(R.id.webview); 584 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 585 helper.loadData("<body style=\"min-height: 120vh; background-color:blue\">"); 586 webview.setVerticalFadingEdgeEnabled(true); 587 webview.setVerticalScrollBarEnabled(false); 588 webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); 589 590 // Adjust Y to match the same gradient percentage, regardless of vertical 591 // fading edge length. 592 int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength(); 593 testPoints[2].y = TEST_HEIGHT 594 - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42); 595 testPoints[3].y = TEST_HEIGHT 596 - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42); 597 }, true, hwFence) 598 .runWithVerifier(new SamplePointVerifier( 599 testPoints, 600 new int[] { 601 Color.BLUE, 602 Color.WHITE, 603 0xffc5c5ff, // white blended with blue 604 0xffdbdbff // white blended with blue 605 }, 50)); 606 } 607 608 @LargeTest 609 @Test testWebViewWithUnclippedLayerAndComplexClip()610 public void testWebViewWithUnclippedLayerAndComplexClip() { 611 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 612 return; // no WebView to run test on 613 } 614 CountDownLatch hwFence = new CountDownLatch(1); 615 Point[] testPoints = new Point[] { 616 // solid white area 617 new Point(0, 0), 618 new Point(0, TEST_HEIGHT - 1), 619 // solid blue area 620 new Point(TEST_WIDTH / 2 , 5), 621 // fade area 622 new Point(TEST_WIDTH / 2, TEST_HEIGHT - 10), 623 new Point(TEST_WIDTH / 2, TEST_HEIGHT - 5) 624 }; 625 createTest() 626 .addLayout(R.layout.circle_clipped_webview, (ViewInitializer) view -> { 627 WebView webview = view.requireViewById(R.id.webview); 628 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 629 helper.loadData("<body style=\"min-height: 120vh; background-color:blue\">"); 630 webview.setVerticalFadingEdgeEnabled(true); 631 webview.setVerticalScrollBarEnabled(false); 632 webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); 633 // Adjust Y to match the same gradient percentage, regardless of vertical 634 // fading edge length. 635 int verticalFadingEdgeLength = webview.getVerticalFadingEdgeLength(); 636 testPoints[3].y = TEST_HEIGHT 637 - (int) Math.round(verticalFadingEdgeLength * 10.0 / 42); 638 testPoints[4].y = TEST_HEIGHT 639 - (int) Math.round(verticalFadingEdgeLength * 5.0 / 42); 640 }, true, hwFence) 641 .runWithVerifier(new SamplePointVerifier( 642 testPoints, 643 new int[] { 644 Color.WHITE, 645 Color.WHITE, 646 Color.BLUE, 647 0xffc5c5ff, // white blended with blue 648 0xffdbdbff // white blended with blue 649 }, 50)); 650 } 651 652 @LargeTest 653 @Test testWebViewOnHWLayerAndComplexAntiAliasedClip()654 public void testWebViewOnHWLayerAndComplexAntiAliasedClip() { 655 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 656 return; // no WebView to run test on 657 } 658 659 CountDownLatch hwFence = new CountDownLatch(1); 660 createTest() 661 // golden client - draw a simple non-AA circle 662 .addCanvasClient((canvas, width, height) -> { 663 Paint paint = new Paint(); 664 paint.setAntiAlias(true); 665 paint.setColor(Color.BLUE); 666 canvas.drawOval(0, 0, width, height, paint); 667 }, false) 668 // verify against solid color webview, clipped to its parent oval 669 .addLayout(R.layout.circle_clipped_webview, (ViewInitializer) view -> { 670 FrameLayout layout = view.requireViewById(R.id.circle_clip_frame_layout); 671 WebView webview = view.requireViewById(R.id.webview); 672 // Promote the webview onto its own layer 673 webview.setLayerType(View.LAYER_TYPE_HARDWARE, null); 674 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 675 helper.loadData("<body style=\"background-color:blue\">"); 676 677 }, true, hwFence) 678 .runWithComparer(new MSSIMComparer(0.98)); 679 } 680 681 @LargeTest 682 @Test testWebViewWithParentLayerAndComplexClip()683 public void testWebViewWithParentLayerAndComplexClip() { 684 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 685 return; // no WebView to run test on 686 } 687 688 CountDownLatch hwFence = new CountDownLatch(1); 689 createTest() 690 // golden client - draw a simple AA circle 691 .addCanvasClient((canvas, width, height) -> { 692 Paint paint = new Paint(); 693 paint.setAntiAlias(true); 694 paint.setColor(Color.BLUE); 695 canvas.drawOval(0, 0, width, height, paint); 696 }, false) 697 // verify against solid color webview, clipped to its parent oval 698 .addLayout(R.layout.circle_clipped_webview, (ViewInitializer) view -> { 699 FrameLayout layout = view.requireViewById(R.id.circle_clip_frame_layout); 700 layout.setLayerType(View.LAYER_TYPE_HARDWARE, null); 701 702 WebView webview = view.requireViewById(R.id.webview); 703 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 704 helper.loadData("<body style=\"background-color:blue\">"); 705 706 }, true, hwFence) 707 // WebView is not on its own layer, so the parent clip may not be AA 708 .runWithComparer(new MSSIMComparer(0.93)); 709 } 710 711 @LargeTest 712 @Test testWebViewWithRRectClip()713 public void testWebViewWithRRectClip() { 714 if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) { 715 return; // no WebView to run test on 716 } 717 718 CountDownLatch hwFence = new CountDownLatch(1); 719 createTest() 720 // golden client - draw an AA rounded rect 721 .addCanvasClient((canvas, width, height) -> { 722 Paint paint = new Paint(); 723 paint.setAntiAlias(true); 724 paint.setColor(Color.BLUE); 725 canvas.drawRoundRect(0, 0, width, height, ActivityTestBase.TEST_WIDTH / 4, 726 ActivityTestBase.TEST_HEIGHT / 4, paint); 727 }, false) 728 // verify against solid color webview, which applies a rounded rect clip 729 .addLayout(R.layout.webview_canvas_rrect_clip, (ViewInitializer) view -> { 730 WebView webview = view.requireViewById(R.id.webview_canvas_rrect_clip); 731 WebViewReadyHelper helper = new WebViewReadyHelper(webview, hwFence); 732 helper.loadData("<body style=\"background-color:blue\">"); 733 }, true, hwFence) 734 .runWithComparer(new MSSIMComparer(0.90)); 735 } 736 } 737