1 /* 2 * Copyright (C) 2016 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.cts; 18 19 import static com.android.compatibility.common.util.SynchronousPixelCopy.copySurface; 20 import static com.android.compatibility.common.util.SynchronousPixelCopy.copyWindow; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotEquals; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertTrue; 26 import static org.mockito.Mockito.mock; 27 import static org.mockito.Mockito.when; 28 29 import android.app.Activity; 30 import android.app.ActivityOptions; 31 import android.app.Instrumentation; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.pm.ActivityInfo; 35 import android.graphics.Bitmap; 36 import android.graphics.Bitmap.Config; 37 import android.graphics.Canvas; 38 import android.graphics.Color; 39 import android.graphics.ColorSpace; 40 import android.graphics.Paint; 41 import android.graphics.PixelFormat; 42 import android.graphics.Rect; 43 import android.graphics.SurfaceTexture; 44 import android.hardware.HardwareBuffer; 45 import android.media.Image; 46 import android.media.ImageReader; 47 import android.media.ImageWriter; 48 import android.os.Debug; 49 import android.server.wm.SetRequestedOrientationRule; 50 import android.util.Half; 51 import android.util.Log; 52 import android.util.Pair; 53 import android.view.PixelCopy; 54 import android.view.Surface; 55 import android.view.View; 56 import android.view.Window; 57 import android.view.WindowManager; 58 import android.view.cts.util.BitmapDumper; 59 60 import androidx.test.InstrumentationRegistry; 61 import androidx.test.filters.LargeTest; 62 import androidx.test.filters.MediumTest; 63 import androidx.test.rule.ActivityTestRule; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import com.android.compatibility.common.util.SynchronousPixelCopy; 67 68 import org.junit.Assert; 69 import org.junit.Before; 70 import org.junit.ClassRule; 71 import org.junit.Rule; 72 import org.junit.Test; 73 import org.junit.rules.ExternalResource; 74 import org.junit.rules.TestName; 75 import org.junit.rules.TestRule; 76 import org.junit.runner.Description; 77 import org.junit.runner.RunWith; 78 import org.junit.runners.model.Statement; 79 80 import java.io.File; 81 import java.nio.ByteBuffer; 82 import java.nio.ByteOrder; 83 import java.util.ArrayList; 84 import java.util.concurrent.CountDownLatch; 85 import java.util.concurrent.TimeUnit; 86 import java.util.function.Function; 87 88 @MediumTest 89 @RunWith(AndroidJUnit4.class) 90 public class PixelCopyTest { 91 private static final String TAG = "PixelCopyTests"; 92 93 @ClassRule 94 public static SetRequestedOrientationRule mSetRequestedOrientationRule = 95 new SetRequestedOrientationRule(); 96 97 @Rule 98 public ActivityTestRule<PixelCopyGLProducerCtsActivity> mGLSurfaceViewActivityRule = 99 new ActivityTestRule<>(PixelCopyGLProducerCtsActivity.class, false, false); 100 101 @Rule 102 public ActivityTestRule<PixelCopyVideoSourceActivity> mVideoSourceActivityRule = 103 new ActivityTestRule<>(PixelCopyVideoSourceActivity.class, false, false); 104 105 public static class FullscreenActivityRule extends ExternalResource { 106 private final ArrayList<Activity> mActivities = new ArrayList<>(); 107 launch(Class<T> klass)108 public <T extends Activity> T launch(Class<T> klass) { 109 final Pair<Intent, ActivityOptions> args = 110 SetRequestedOrientationRule.buildFullScreenLaunchArgs(klass); 111 final T activity = (T) InstrumentationRegistry.getInstrumentation() 112 .startActivitySync(args.first, args.second.toBundle()); 113 mActivities.add(activity); 114 return activity; 115 } 116 117 @Override after()118 protected void after() { 119 if (mActivities.isEmpty()) return; 120 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { 121 for (final Activity activity : mActivities) { 122 activity.finish(); 123 } 124 }); 125 } 126 } 127 128 @Rule 129 public FullscreenActivityRule mFullscreenActivityRule = new FullscreenActivityRule(); 130 131 @Rule 132 public SurfaceTextureRule mSurfaceRule = new SurfaceTextureRule(); 133 134 @Rule 135 public TestName mTestName = new TestName(); 136 137 private Instrumentation mInstrumentation; 138 private SynchronousPixelCopy mCopyHelper; 139 140 @Before setup()141 public void setup() { 142 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 143 assertNotNull(mInstrumentation); 144 mCopyHelper = new SynchronousPixelCopy(); 145 } 146 147 @Test(expected = IllegalArgumentException.class) testNullDest()148 public void testNullDest() { 149 Bitmap dest = null; 150 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 151 } 152 153 @Test(expected = IllegalArgumentException.class) testRecycledDest()154 public void testRecycledDest() { 155 Bitmap dest = Bitmap.createBitmap(5, 5, Config.ARGB_8888); 156 dest.recycle(); 157 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 158 } 159 160 @Test testNoSourceData()161 public void testNoSourceData() { 162 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 163 int result = mCopyHelper.request(mSurfaceRule.getSurface(), dest); 164 assertEquals(PixelCopy.ERROR_SOURCE_NO_DATA, result); 165 } 166 167 @Test(expected = IllegalArgumentException.class) testEmptySourceRectSurface()168 public void testEmptySourceRectSurface() { 169 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 170 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(), dest); 171 } 172 173 @Test(expected = IllegalArgumentException.class) testEmptySourceRectWindow()174 public void testEmptySourceRectWindow() { 175 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 176 mCopyHelper.request(mock(Window.class), new Rect(), dest); 177 } 178 179 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectSurface()180 public void testInvalidSourceRectSurface() { 181 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 182 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(10, 10, 0, 0), dest); 183 } 184 185 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectWindow()186 public void testInvalidSourceRectWindow() { 187 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 188 mCopyHelper.request(mock(Window.class), new Rect(10, 10, 0, 0), dest); 189 } 190 191 @Test(expected = IllegalArgumentException.class) testNoDecorView()192 public void testNoDecorView() { 193 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 194 Window mockWindow = mock(Window.class); 195 mCopyHelper.request(mockWindow, dest); 196 } 197 198 @Test(expected = IllegalArgumentException.class) testNoViewRoot()199 public void testNoViewRoot() { 200 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 201 Window mockWindow = mock(Window.class); 202 View view = new View(mInstrumentation.getTargetContext()); 203 when(mockWindow.peekDecorView()).thenReturn(view); 204 mCopyHelper.request(mockWindow, dest); 205 } 206 207 @Test testRequestGetters()208 public void testRequestGetters() { 209 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 210 Bitmap dest = Bitmap.createBitmap(5, 5, Config.ARGB_8888); 211 Rect source = new Rect(3, 3, 40, 50); 212 PixelCopy.Request request = PixelCopy.Request.Builder.ofWindow(activity.getWindow()) 213 .setSourceRect(source) 214 .setDestinationBitmap(dest) 215 .build(); 216 assertEquals(dest, request.getDestinationBitmap()); 217 assertEquals(source, request.getSourceRect()); 218 } 219 waitForGlProducerActivity()220 private PixelCopyGLProducerCtsActivity waitForGlProducerActivity() { 221 CountDownLatch swapFence = new CountDownLatch(2); 222 223 PixelCopyGLProducerCtsActivity activity = 224 mGLSurfaceViewActivityRule.launchActivity(null); 225 activity.setSwapFence(swapFence); 226 227 try { 228 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 229 activity.getView().requestRender(); 230 } 231 } catch (InterruptedException ex) { 232 Assert.fail("Interrupted, error=" + ex.getMessage()); 233 } 234 return activity; 235 } 236 237 @Test testGlProducerFullsize()238 public void testGlProducerFullsize() { 239 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 240 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 241 int result = mCopyHelper.request(activity.getView(), bitmap); 242 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 243 assertEquals(100, bitmap.getWidth()); 244 assertEquals(100, bitmap.getHeight()); 245 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 246 assertBitmapQuadColor(bitmap, 247 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 248 } 249 250 @Test testGlProducerAutoSize()251 public void testGlProducerAutoSize() { 252 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 253 PixelCopy.Result result = copySurface(activity.getView()); 254 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result.getStatus()); 255 Bitmap bitmap = result.getBitmap(); 256 assertEquals(100, bitmap.getWidth()); 257 assertEquals(100, bitmap.getHeight()); 258 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 259 assertBitmapQuadColor(bitmap, 260 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 261 } 262 263 @Test testGlProducerCropTopLeft()264 public void testGlProducerCropTopLeft() { 265 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 266 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 267 int result = mCopyHelper.request(activity.getView(), new Rect(0, 0, 50, 50), bitmap); 268 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 269 assertBitmapQuadColor(bitmap, 270 Color.RED, Color.RED, Color.RED, Color.RED); 271 } 272 273 @Test testGlProducerCropCenter()274 public void testGlProducerCropCenter() { 275 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 276 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 277 int result = mCopyHelper.request(activity.getView(), new Rect(25, 25, 75, 75), bitmap); 278 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 279 assertBitmapQuadColor(bitmap, 280 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 281 } 282 283 @Test testGlProducerCropBottomHalf()284 public void testGlProducerCropBottomHalf() { 285 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 286 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 287 int result = mCopyHelper.request(activity.getView(), new Rect(0, 50, 100, 100), bitmap); 288 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 289 assertBitmapQuadColor(bitmap, 290 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 291 } 292 293 @Test testGlProducerCropClamping()294 public void testGlProducerCropClamping() { 295 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 296 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 297 int result = mCopyHelper.request(activity.getView(), new Rect(50, -50, 150, 50), bitmap); 298 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 299 assertBitmapQuadColor(bitmap, 300 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN); 301 } 302 303 @Test testGlProducerScaling()304 public void testGlProducerScaling() { 305 // Since we only sample mid-pixel of each qudrant, filtering 306 // quality isn't tested 307 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 308 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 309 int result = mCopyHelper.request(activity.getView(), bitmap); 310 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 311 // Make sure nothing messed with the bitmap 312 assertEquals(20, bitmap.getWidth()); 313 assertEquals(20, bitmap.getHeight()); 314 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 315 assertBitmapQuadColor(bitmap, 316 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 317 } 318 319 @Test testReuseBitmap()320 public void testReuseBitmap() { 321 // Since we only sample mid-pixel of each qudrant, filtering 322 // quality isn't tested 323 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 324 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 325 int result = mCopyHelper.request(activity.getView(), bitmap); 326 // Make sure nothing messed with the bitmap 327 assertEquals(20, bitmap.getWidth()); 328 assertEquals(20, bitmap.getHeight()); 329 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 330 assertBitmapQuadColor(bitmap, 331 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 332 int generationId = bitmap.getGenerationId(); 333 result = mCopyHelper.request(activity.getView(), bitmap); 334 // Make sure nothing messed with the bitmap 335 assertEquals(20, bitmap.getWidth()); 336 assertEquals(20, bitmap.getHeight()); 337 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 338 assertBitmapQuadColor(bitmap, 339 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 340 assertNotEquals(generationId, bitmap.getGenerationId()); 341 } 342 waitForWindowProducerActivity()343 private PixelCopyViewProducerActivity waitForWindowProducerActivity() { 344 PixelCopyViewProducerActivity activity = mFullscreenActivityRule.launch( 345 PixelCopyViewProducerActivity.class); 346 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 347 return activity; 348 } 349 makeWindowRect( PixelCopyViewProducerActivity activity, int left, int top, int right, int bottom)350 private Rect makeWindowRect( 351 PixelCopyViewProducerActivity activity, int left, int top, int right, int bottom) { 352 Rect r = new Rect(left, top, right, bottom); 353 activity.normalizedToSurface(r); 354 return r; 355 } 356 357 @Test testViewProducer()358 public void testViewProducer() { 359 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 360 do { 361 final Rect src = makeWindowRect(activity, 0, 0, 100, 100); 362 final Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), 363 Config.ARGB_8888); 364 int result = copyWindow(activity.getContentView(), request -> { 365 request.setDestinationBitmap(bitmap); 366 request.setSourceRect(src); 367 }).getStatus(); 368 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 369 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 370 assertBitmapQuadColor(bitmap, 371 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 372 assertBitmapEdgeColor(bitmap, Color.YELLOW); 373 } while (activity.rotate()); 374 } 375 376 @Test testWindowProducerAutoSize()377 public void testWindowProducerAutoSize() { 378 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 379 Window window = activity.getWindow(); 380 do { 381 PixelCopy.Result result = copyWindow(window); 382 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, 383 result.getStatus()); 384 final Bitmap bitmap = result.getBitmap(); 385 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 386 final View decorView = window.getDecorView(); 387 assertTrue(bitmap.getWidth() >= decorView.getWidth()); 388 assertTrue(bitmap.getHeight() >= decorView.getHeight()); 389 // We can't directly assert qualities of the bitmap because the View's location 390 // is going to be affected by padding/insets. 391 } while (activity.rotate()); 392 } 393 394 @Test testViewProducerAutoSizeWithSrc()395 public void testViewProducerAutoSizeWithSrc() { 396 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 397 do { 398 final Rect src = makeWindowRect(activity, 0, 0, 100, 100); 399 PixelCopy.Result result = copyWindow(activity.getContentView(), request -> { 400 request.setSourceRect(src); 401 }); 402 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result.getStatus()); 403 final Bitmap bitmap = result.getBitmap(); 404 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 405 assertEquals(src.width(), bitmap.getWidth()); 406 assertEquals(src.height(), bitmap.getHeight()); 407 assertBitmapQuadColor(bitmap, 408 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 409 assertBitmapEdgeColor(bitmap, Color.YELLOW); 410 } while (activity.rotate()); 411 } 412 413 @Test testWindowProducerCropTopLeft()414 public void testWindowProducerCropTopLeft() { 415 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 416 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 417 do { 418 int result = mCopyHelper.request( 419 activity.getWindow(), makeWindowRect(activity, 0, 0, 50, 50), bitmap); 420 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 421 assertBitmapQuadColor(bitmap, 422 Color.RED, Color.RED, Color.RED, Color.RED); 423 } while (activity.rotate()); 424 } 425 426 @Test testWindowProducerCropCenter()427 public void testWindowProducerCropCenter() { 428 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 429 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 430 do { 431 int result = mCopyHelper.request( 432 activity.getWindow(), makeWindowRect(activity, 25, 25, 75, 75), bitmap); 433 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 434 assertBitmapQuadColor(bitmap, 435 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 436 } while (activity.rotate()); 437 } 438 439 @Test testWindowProducerCropBottomHalf()440 public void testWindowProducerCropBottomHalf() { 441 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 442 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 443 do { 444 int result = mCopyHelper.request( 445 activity.getWindow(), makeWindowRect(activity, 0, 50, 100, 100), bitmap); 446 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 447 assertBitmapQuadColor(bitmap, 448 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 449 } while (activity.rotate()); 450 } 451 452 @Test testWindowProducerScaling()453 public void testWindowProducerScaling() { 454 // Since we only sample mid-pixel of each qudrant, filtering 455 // quality isn't tested 456 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 457 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 458 do { 459 int result = mCopyHelper.request(activity.getWindow(), bitmap); 460 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 461 // Make sure nothing messed with the bitmap 462 assertEquals(20, bitmap.getWidth()); 463 assertEquals(20, bitmap.getHeight()); 464 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 465 assertBitmapQuadColor(bitmap, 466 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 467 } while (activity.rotate()); 468 } 469 470 @Test testWindowProducerCopyToRGBA16F()471 public void testWindowProducerCopyToRGBA16F() { 472 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 473 do { 474 Rect src = makeWindowRect(activity, 0, 0, 100, 100); 475 Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16); 476 int result = mCopyHelper.request(activity.getWindow(), src, bitmap); 477 // On OpenGL ES 2.0 devices a copy to RGBA_F16 can fail because there's 478 // not support for float textures 479 if (result != PixelCopy.ERROR_DESTINATION_INVALID) { 480 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 481 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 482 assertBitmapQuadColor(bitmap, 483 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 484 assertBitmapEdgeColor(bitmap, Color.YELLOW); 485 } 486 } while (activity.rotate()); 487 } 488 489 @Test testWindowProducer()490 public void testWindowProducer() { 491 Bitmap bitmap; 492 PixelCopyViewProducerActivity activity = waitForWindowProducerActivity(); 493 Window window = activity.getWindow(); 494 do { 495 Rect src = makeWindowRect(activity, 0, 0, 100, 100); 496 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 497 int result = mCopyHelper.request(window, src, bitmap); 498 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 499 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 500 assertBitmapQuadColor(bitmap, 501 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 502 assertBitmapEdgeColor(bitmap, Color.YELLOW); 503 } while (activity.rotate()); 504 } 505 waitForWideGamutWindowProducerActivity()506 private PixelCopyWideGamutViewProducerActivity waitForWideGamutWindowProducerActivity() { 507 PixelCopyWideGamutViewProducerActivity activity = mFullscreenActivityRule.launch( 508 PixelCopyWideGamutViewProducerActivity.class); 509 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 510 return activity; 511 } 512 makeWideGamutWindowRect( PixelCopyWideGamutViewProducerActivity activity, int left, int top, int right, int bottom)513 private Rect makeWideGamutWindowRect( 514 PixelCopyWideGamutViewProducerActivity activity, 515 int left, int top, int right, int bottom) { 516 Rect r = new Rect(left, top, right, bottom); 517 activity.offsetForContent(r); 518 return r; 519 } 520 521 @Test testWideGamutWindowProducerCopyToRGBA8888()522 public void testWideGamutWindowProducerCopyToRGBA8888() { 523 PixelCopyWideGamutViewProducerActivity activity = waitForWideGamutWindowProducerActivity(); 524 assertEquals( 525 ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT, 526 activity.getWindow().getAttributes().getColorMode() 527 ); 528 529 // Early out if the device does not support wide color gamut rendering 530 if (!activity.getWindow().isWideColorGamut()) { 531 return; 532 } 533 534 do { 535 Rect src = makeWideGamutWindowRect(activity, 0, 0, 128, 128); 536 Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 537 int result = mCopyHelper.request(activity.getWindow(), src, bitmap); 538 539 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 540 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 541 542 assertEquals("Top left", Color.RED, bitmap.getPixel(32, 32)); 543 assertEquals("Top right", Color.GREEN, bitmap.getPixel(96, 32)); 544 assertEquals("Bottom left", Color.BLUE, bitmap.getPixel(32, 96)); 545 assertEquals("Bottom right", Color.YELLOW, bitmap.getPixel(96, 96)); 546 } while (activity.rotate()); 547 } 548 549 @Test testWideGamutWindowProducerCopyToRGBA16F()550 public void testWideGamutWindowProducerCopyToRGBA16F() { 551 PixelCopyWideGamutViewProducerActivity activity = waitForWideGamutWindowProducerActivity(); 552 assertEquals( 553 ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT, 554 activity.getWindow().getAttributes().getColorMode() 555 ); 556 557 // Early out if the device does not support wide color gamut rendering 558 if (!activity.getWindow().isWideColorGamut()) { 559 return; 560 } 561 562 final WindowManager windowManager = (WindowManager) activity.getSystemService( 563 Context.WINDOW_SERVICE); 564 final ColorSpace colorSpace = windowManager.getDefaultDisplay() 565 .getPreferredWideGamutColorSpace(); 566 final ColorSpace.Connector proPhotoToDisplayWideColorSpace = ColorSpace.connect( 567 ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), colorSpace); 568 final ColorSpace.Connector displayWideColorSpaceToExtendedSrgb = ColorSpace.connect( 569 colorSpace, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); 570 571 final float[] intermediateRed = 572 proPhotoToDisplayWideColorSpace.transform(1.0f, 0.0f, 0.0f); 573 final float[] intermediateGreen = proPhotoToDisplayWideColorSpace 574 .transform(0.0f, 1.0f, 0.0f); 575 final float[] intermediateBlue = proPhotoToDisplayWideColorSpace 576 .transform(0.0f, 0.0f, 1.0f); 577 final float[] intermediateYellow = proPhotoToDisplayWideColorSpace 578 .transform(1.0f, 1.0f, 0.0f); 579 580 final float[] expectedRed = 581 displayWideColorSpaceToExtendedSrgb.transform(intermediateRed); 582 final float[] expectedGreen = displayWideColorSpaceToExtendedSrgb 583 .transform(intermediateGreen); 584 final float[] expectedBlue = displayWideColorSpaceToExtendedSrgb 585 .transform(intermediateBlue); 586 final float[] expectedYellow = displayWideColorSpaceToExtendedSrgb 587 .transform(intermediateYellow); 588 589 do { 590 Rect src = makeWideGamutWindowRect(activity, 0, 0, 128, 128); 591 Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16, 592 true, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); 593 int result = mCopyHelper.request(activity.getWindow(), src, bitmap); 594 595 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 596 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 597 598 ByteBuffer dst = ByteBuffer.allocateDirect(bitmap.getAllocationByteCount()); 599 bitmap.copyPixelsToBuffer(dst); 600 dst.rewind(); 601 dst.order(ByteOrder.LITTLE_ENDIAN); 602 603 // ProPhoto RGB red in scRGB-nl 604 assertEqualsRgba16f("Top left", bitmap, 32, 32, dst, expectedRed[0], 605 expectedRed[1], expectedRed[2], 1.0f); 606 // ProPhoto RGB green in scRGB-nl 607 assertEqualsRgba16f("Top right", bitmap, 96, 32, dst, 608 expectedGreen[0], expectedGreen[1], expectedGreen[2], 1.0f); 609 // ProPhoto RGB blue in scRGB-nl 610 assertEqualsRgba16f("Bottom left", bitmap, 32, 96, dst, 611 expectedBlue[0], expectedBlue[1], expectedBlue[2], 1.0f); 612 // ProPhoto RGB yellow in scRGB-nl 613 assertEqualsRgba16f("Bottom right", bitmap, 96, 96, dst, 614 expectedYellow[0], expectedYellow[1], expectedYellow[2], 1.0f); 615 } while (activity.rotate()); 616 } 617 waitForDialogProducerActivity()618 private PixelCopyViewProducerDialogActivity waitForDialogProducerActivity() { 619 PixelCopyViewProducerDialogActivity activity = mFullscreenActivityRule.launch( 620 PixelCopyViewProducerDialogActivity.class); 621 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 622 return activity; 623 } 624 makeDialogRect( PixelCopyViewProducerDialogActivity activity, int left, int top, int right, int bottom)625 private Rect makeDialogRect( 626 PixelCopyViewProducerDialogActivity activity, 627 int left, int top, int right, int bottom) { 628 Rect r = new Rect(left, top, right, bottom); 629 activity.normalizedToSurface(r); 630 return r; 631 } 632 633 @Test testDialogProducer()634 public void testDialogProducer() { 635 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 636 do { 637 Rect src = makeDialogRect(activity, 0, 0, 100, 100); 638 Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 639 int result = mCopyHelper.request(activity.getWindow(), src, bitmap); 640 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 641 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 642 assertBitmapQuadColor(bitmap, 643 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 644 assertBitmapEdgeColor(bitmap, Color.YELLOW); 645 } while (activity.rotate()); 646 } 647 648 @Test testDialogProducerCropTopLeft()649 public void testDialogProducerCropTopLeft() { 650 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 651 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 652 do { 653 int result = mCopyHelper.request( 654 activity.getWindow(), makeDialogRect(activity, 0, 0, 50, 50), bitmap); 655 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 656 assertBitmapQuadColor(bitmap, 657 Color.RED, Color.RED, Color.RED, Color.RED); 658 } while (activity.rotate()); 659 } 660 661 @Test testDialogProducerCropCenter()662 public void testDialogProducerCropCenter() { 663 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 664 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 665 do { 666 int result = mCopyHelper.request( 667 activity.getWindow(), makeDialogRect(activity, 25, 25, 75, 75), bitmap); 668 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 669 assertBitmapQuadColor(bitmap, 670 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 671 } while (activity.rotate()); 672 } 673 674 @Test testDialogProducerCropBottomHalf()675 public void testDialogProducerCropBottomHalf() { 676 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 677 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 678 do { 679 int result = mCopyHelper.request( 680 activity.getWindow(), makeDialogRect(activity, 0, 50, 100, 100), bitmap); 681 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 682 assertBitmapQuadColor(bitmap, 683 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 684 } while (activity.rotate()); 685 } 686 687 @Test testDialogProducerScaling()688 public void testDialogProducerScaling() { 689 // Since we only sample mid-pixel of each qudrant, filtering 690 // quality isn't tested 691 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 692 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 693 do { 694 int result = mCopyHelper.request(activity.getWindow(), bitmap); 695 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 696 // Make sure nothing messed with the bitmap 697 assertEquals(20, bitmap.getWidth()); 698 assertEquals(20, bitmap.getHeight()); 699 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 700 assertBitmapQuadColor(bitmap, 701 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 702 } while (activity.rotate()); 703 } 704 705 @Test testDialogProducerCopyToRGBA16F()706 public void testDialogProducerCopyToRGBA16F() { 707 PixelCopyViewProducerDialogActivity activity = waitForDialogProducerActivity(); 708 do { 709 Rect src = makeDialogRect(activity, 0, 0, 100, 100); 710 Bitmap bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16); 711 int result = mCopyHelper.request(activity.getWindow(), src, bitmap); 712 // On OpenGL ES 2.0 devices a copy to RGBA_F16 can fail because there's 713 // not support for float textures 714 if (result != PixelCopy.ERROR_DESTINATION_INVALID) { 715 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 716 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 717 assertBitmapQuadColor(bitmap, 718 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 719 assertBitmapEdgeColor(bitmap, Color.YELLOW); 720 } 721 } while (activity.rotate()); 722 } 723 assertEqualsRgba16f(String message, Bitmap bitmap, int x, int y, ByteBuffer dst, float r, float g, float b, float a)724 private static void assertEqualsRgba16f(String message, Bitmap bitmap, int x, int y, 725 ByteBuffer dst, float r, float g, float b, float a) { 726 int index = y * bitmap.getRowBytes() + (x << 3); 727 short cR = dst.getShort(index); 728 short cG = dst.getShort(index + 2); 729 short cB = dst.getShort(index + 4); 730 short cA = dst.getShort(index + 6); 731 732 assertEquals(message, r, Half.toFloat(cR), 0.01); 733 assertEquals(message, g, Half.toFloat(cG), 0.01); 734 assertEquals(message, b, Half.toFloat(cB), 0.01); 735 assertEquals(message, a, Half.toFloat(cA), 0.01); 736 } 737 runGcAndFinalizersSync()738 private static void runGcAndFinalizersSync() { 739 Runtime.getRuntime().gc(); 740 Runtime.getRuntime().runFinalization(); 741 742 final CountDownLatch fence = new CountDownLatch(1); 743 new Object() { 744 @Override 745 protected void finalize() throws Throwable { 746 try { 747 fence.countDown(); 748 } finally { 749 super.finalize(); 750 } 751 } 752 }; 753 try { 754 do { 755 Runtime.getRuntime().gc(); 756 Runtime.getRuntime().runFinalization(); 757 } while (!fence.await(100, TimeUnit.MILLISECONDS)); 758 } catch (InterruptedException ex) { 759 throw new RuntimeException(ex); 760 } 761 } 762 763 private static File sProcSelfFd = new File("/proc/self/fd"); getFdCount()764 private static int getFdCount() { 765 return sProcSelfFd.listFiles().length; 766 } 767 assertNotLeaking(int iteration, Debug.MemoryInfo start, Debug.MemoryInfo end)768 private static void assertNotLeaking(int iteration, 769 Debug.MemoryInfo start, Debug.MemoryInfo end) { 770 Debug.getMemoryInfo(end); 771 assertNotEquals(0, start.getTotalPss()); 772 assertNotEquals(0, end.getTotalPss()); 773 if (end.getTotalPss() - start.getTotalPss() > 5000 /* kB */) { 774 runGcAndFinalizersSync(); 775 Debug.getMemoryInfo(end); 776 if (end.getTotalPss() - start.getTotalPss() > 7000 /* kB */) { 777 // Guarded by if so we don't continually generate garbage for the 778 // assertion string. 779 assertEquals("Memory leaked, iteration=" + iteration, 780 start.getTotalPss(), end.getTotalPss(), 781 7000 /* kb */); 782 } 783 } 784 } 785 runNotLeakingTest(Runnable test)786 private static void runNotLeakingTest(Runnable test) { 787 Debug.MemoryInfo meminfoStart = new Debug.MemoryInfo(); 788 Debug.MemoryInfo meminfoEnd = new Debug.MemoryInfo(); 789 int fdCount = -1; 790 // Do a warmup to reach steady-state memory usage 791 for (int i = 0; i < 50; i++) { 792 test.run(); 793 } 794 runGcAndFinalizersSync(); 795 Debug.getMemoryInfo(meminfoStart); 796 fdCount = getFdCount(); 797 // Now run the test 798 for (int i = 0; i < 2000; i++) { 799 if (i % 100 == 5) { 800 assertNotLeaking(i, meminfoStart, meminfoEnd); 801 final int curFdCount = getFdCount(); 802 if (curFdCount - fdCount > 10) { 803 Assert.fail(String.format("FDs leaked. Expected=%d, current=%d, iteration=%d", 804 fdCount, curFdCount, i)); 805 } 806 } 807 test.run(); 808 } 809 assertNotLeaking(2000, meminfoStart, meminfoEnd); 810 final int curFdCount = getFdCount(); 811 if (curFdCount - fdCount > 10) { 812 Assert.fail(String.format("FDs leaked. Expected=%d, current=%d", fdCount, curFdCount)); 813 } 814 } 815 816 @Test 817 @LargeTest testNotLeaking()818 public void testNotLeaking() { 819 try { 820 CountDownLatch swapFence = new CountDownLatch(2); 821 822 PixelCopyGLProducerCtsActivity activity = 823 mGLSurfaceViewActivityRule.launchActivity(null); 824 activity.setSwapFence(swapFence); 825 826 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 827 activity.getView().requestRender(); 828 } 829 830 // Test a fullsize copy 831 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 832 833 runNotLeakingTest(() -> { 834 int result = mCopyHelper.request(activity.getView(), bitmap); 835 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 836 // Make sure nothing messed with the bitmap 837 assertEquals(100, bitmap.getWidth()); 838 assertEquals(100, bitmap.getHeight()); 839 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 840 assertBitmapQuadColor(bitmap, 841 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 842 }); 843 844 } catch (InterruptedException e) { 845 Assert.fail("Interrupted, error=" + e.getMessage()); 846 } 847 } 848 849 @Test testVideoProducer()850 public void testVideoProducer() throws InterruptedException { 851 PixelCopyVideoSourceActivity activity = 852 mVideoSourceActivityRule.launchActivity(null); 853 854 Thread.sleep(2000); 855 856 if (!activity.canPlayVideo()) { 857 Log.i(TAG, "Skipping testVideoProducer, video codec isn't supported"); 858 return; 859 } 860 // This returns when the video has been prepared and playback has 861 // been started, it doesn't necessarily means a frame has actually been 862 // produced. There sadly isn't a callback for that. 863 // So we'll try for up to 900ms after this event to acquire a frame, otherwise 864 // it's considered a timeout. 865 activity.waitForPlaying(); 866 assertTrue("Failed to start video playback", activity.canPlayVideo()); 867 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 868 int copyResult = PixelCopy.ERROR_SOURCE_NO_DATA; 869 for (int i = 0; i < 30; i++) { 870 copyResult = mCopyHelper.request(activity.getVideoView(), bitmap); 871 if (copyResult != PixelCopy.ERROR_SOURCE_NO_DATA) { 872 break; 873 } 874 Thread.sleep(30); 875 } 876 assertEquals(PixelCopy.SUCCESS, copyResult); 877 // A large threshold is used because decoder accuracy is covered in the 878 // media CTS tests, so we are mainly interested in verifying that rotation 879 // and YUV->RGB conversion were handled properly. 880 assertBitmapQuadColor(bitmap, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 881 882 // Test that cropping works. 883 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 0, 50, 50), bitmap); 884 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 885 assertBitmapQuadColor(bitmap, 886 Color.RED, Color.RED, Color.RED, Color.RED, 30); 887 888 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(50, 0, 100, 50), bitmap); 889 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 890 assertBitmapQuadColor(bitmap, 891 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, 30); 892 893 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 50, 50, 100), bitmap); 894 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 895 assertBitmapQuadColor(bitmap, 896 Color.BLUE, Color.BLUE, Color.BLUE, Color.BLUE, 30); 897 898 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(50, 50, 100, 100), bitmap); 899 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 900 assertBitmapQuadColor(bitmap, 901 Color.BLACK, Color.BLACK, Color.BLACK, Color.BLACK, 30); 902 903 904 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(25, 25, 75, 75), bitmap); 905 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 906 assertBitmapQuadColor(bitmap, 907 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 908 909 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 50, 100, 100), bitmap); 910 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 911 assertBitmapQuadColor(bitmap, 912 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK, 30); 913 914 // Test that clamping works 915 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(50, -50, 150, 50), bitmap); 916 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 917 assertBitmapQuadColor(bitmap, 918 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, 30); 919 } 920 921 @Test testBufferQueueCrop()922 public void testBufferQueueCrop() throws InterruptedException { 923 ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 1, 924 HardwareBuffer.USAGE_CPU_WRITE_OFTEN | HardwareBuffer.USAGE_CPU_READ_OFTEN 925 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 926 ImageWriter writer = ImageWriter.newInstance(reader.getSurface(), 1); 927 Image image = writer.dequeueInputImage(); 928 Image.Plane plane = image.getPlanes()[0]; 929 Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, 930 image.getHeight(), Bitmap.Config.ARGB_8888); 931 Canvas canvas = new Canvas(bitmap); 932 Paint paint = new Paint(); 933 paint.setAntiAlias(false); 934 canvas.drawColor(Color.MAGENTA); 935 canvas.translate(20f, 70f); 936 paint.setColor(Color.RED); 937 canvas.drawRect(0f, 0f, 10f, 10f, paint); 938 paint.setColor(Color.GREEN); 939 canvas.drawRect(10f, 0f, 20f, 10f, paint); 940 paint.setColor(Color.BLUE); 941 canvas.drawRect(0f, 10f, 10f, 20f, paint); 942 paint.setColor(Color.BLACK); 943 canvas.drawRect(10f, 10f, 20f, 20f, paint); 944 bitmap.copyPixelsToBuffer(plane.getBuffer()); 945 image.setCropRect(new Rect(20, 70, 40, 90)); 946 writer.queueInputImage(image); 947 948 // implicit size 949 Bitmap result = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 950 int status = mCopyHelper.request(reader.getSurface(), result); 951 assertEquals("Copy request failed", PixelCopy.SUCCESS, status); 952 assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 953 954 // specified size 955 result = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 956 status = mCopyHelper.request(reader.getSurface(), new Rect(0, 0, 20, 20), result); 957 assertEquals("Copy request failed", PixelCopy.SUCCESS, status); 958 assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 959 } 960 961 @Test testAutoSize()962 public void testAutoSize() throws InterruptedException { 963 ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 1, 964 HardwareBuffer.USAGE_CPU_WRITE_OFTEN | HardwareBuffer.USAGE_CPU_READ_OFTEN 965 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 966 ImageWriter writer = ImageWriter.newInstance(reader.getSurface(), 1); 967 Image image = writer.dequeueInputImage(); 968 Image.Plane plane = image.getPlanes()[0]; 969 Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, 970 image.getHeight(), Bitmap.Config.ARGB_8888); 971 Canvas canvas = new Canvas(bitmap); 972 Paint paint = new Paint(); 973 paint.setAntiAlias(false); 974 paint.setColor(Color.RED); 975 canvas.drawRect(0f, 0f, 50f, 50f, paint); 976 paint.setColor(Color.GREEN); 977 canvas.drawRect(50f, 0f, 100f, 50f, paint); 978 paint.setColor(Color.BLUE); 979 canvas.drawRect(0f, 50f, 50f, 100f, paint); 980 paint.setColor(Color.BLACK); 981 canvas.drawRect(50f, 50f, 100f, 100f, paint); 982 bitmap.copyPixelsToBuffer(plane.getBuffer()); 983 writer.queueInputImage(image); 984 985 PixelCopy.Result copyResult = copySurface(reader.getSurface()); 986 assertEquals("Copy request failed", PixelCopy.SUCCESS, copyResult.getStatus()); 987 Bitmap result = copyResult.getBitmap(); 988 assertEquals(100, result.getWidth()); 989 assertEquals(100, result.getHeight()); 990 assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 991 } 992 993 @Test testAutoSizeWithCrop()994 public void testAutoSizeWithCrop() throws InterruptedException { 995 ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 1, 996 HardwareBuffer.USAGE_CPU_WRITE_OFTEN | HardwareBuffer.USAGE_CPU_READ_OFTEN 997 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 998 ImageWriter writer = ImageWriter.newInstance(reader.getSurface(), 1); 999 Image image = writer.dequeueInputImage(); 1000 Image.Plane plane = image.getPlanes()[0]; 1001 Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, 1002 image.getHeight(), Bitmap.Config.ARGB_8888); 1003 Canvas canvas = new Canvas(bitmap); 1004 Paint paint = new Paint(); 1005 paint.setAntiAlias(false); 1006 canvas.drawColor(Color.MAGENTA); 1007 canvas.translate(20f, 70f); 1008 paint.setColor(Color.RED); 1009 canvas.drawRect(0f, 0f, 10f, 10f, paint); 1010 paint.setColor(Color.GREEN); 1011 canvas.drawRect(10f, 0f, 20f, 10f, paint); 1012 paint.setColor(Color.BLUE); 1013 canvas.drawRect(0f, 10f, 10f, 20f, paint); 1014 paint.setColor(Color.BLACK); 1015 canvas.drawRect(10f, 10f, 20f, 20f, paint); 1016 bitmap.copyPixelsToBuffer(plane.getBuffer()); 1017 image.setCropRect(new Rect(20, 70, 40, 90)); 1018 writer.queueInputImage(image); 1019 1020 PixelCopy.Result copyResult = copySurface(reader.getSurface()); 1021 assertEquals("Copy request failed", PixelCopy.SUCCESS, copyResult.getStatus()); 1022 Bitmap result = copyResult.getBitmap(); 1023 assertEquals(20, result.getWidth()); 1024 assertEquals(20, result.getHeight()); 1025 assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 1026 } 1027 1028 @Test testAutoSizeWithSrcRect()1029 public void testAutoSizeWithSrcRect() throws InterruptedException { 1030 ImageReader reader = ImageReader.newInstance(100, 100, PixelFormat.RGBA_8888, 1, 1031 HardwareBuffer.USAGE_CPU_WRITE_OFTEN | HardwareBuffer.USAGE_CPU_READ_OFTEN 1032 | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE); 1033 ImageWriter writer = ImageWriter.newInstance(reader.getSurface(), 1); 1034 Image image = writer.dequeueInputImage(); 1035 Image.Plane plane = image.getPlanes()[0]; 1036 Bitmap bitmap = Bitmap.createBitmap(plane.getRowStride() / 4, 1037 image.getHeight(), Bitmap.Config.ARGB_8888); 1038 Canvas canvas = new Canvas(bitmap); 1039 Paint paint = new Paint(); 1040 paint.setAntiAlias(false); 1041 canvas.drawColor(Color.MAGENTA); 1042 canvas.translate(20f, 70f); 1043 paint.setColor(Color.RED); 1044 canvas.drawRect(0f, 0f, 10f, 10f, paint); 1045 paint.setColor(Color.GREEN); 1046 canvas.drawRect(10f, 0f, 20f, 10f, paint); 1047 paint.setColor(Color.BLUE); 1048 canvas.drawRect(0f, 10f, 10f, 20f, paint); 1049 paint.setColor(Color.BLACK); 1050 canvas.drawRect(10f, 10f, 20f, 20f, paint); 1051 bitmap.copyPixelsToBuffer(plane.getBuffer()); 1052 writer.queueInputImage(image); 1053 1054 PixelCopy.Result copyResult = copySurface(reader.getSurface(), 1055 request -> request.setSourceRect(new Rect(20, 70, 40, 90))); 1056 assertEquals("Copy request failed", PixelCopy.SUCCESS, copyResult.getStatus()); 1057 Bitmap result = copyResult.getBitmap(); 1058 assertEquals(20, result.getWidth()); 1059 assertEquals(20, result.getHeight()); 1060 assertBitmapQuadColor(result, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 1061 } 1062 getPixelFloatPos(Bitmap bitmap, float xpos, float ypos)1063 private static int getPixelFloatPos(Bitmap bitmap, float xpos, float ypos) { 1064 return bitmap.getPixel((int) (bitmap.getWidth() * xpos), (int) (bitmap.getHeight() * ypos)); 1065 } 1066 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight)1067 private void assertBitmapQuadColor(Bitmap bitmap, 1068 int topLeft, int topRight, int bottomLeft, int bottomRight) { 1069 assertBitmapQuadColor(mTestName.getMethodName(), "PixelCopyTest", bitmap, 1070 topLeft, topRight, bottomLeft, bottomRight); 1071 } 1072 assertBitmapQuadColor(String testName, String className, Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight)1073 public static void assertBitmapQuadColor(String testName, String className, Bitmap bitmap, 1074 int topLeft, int topRight, int bottomLeft, int bottomRight) { 1075 try { 1076 // Just quickly sample 4 pixels in the various regions. 1077 assertEquals("Top left " + Integer.toHexString(topLeft) + ", actual= " 1078 + Integer.toHexString(getPixelFloatPos(bitmap, .25f, .25f)), 1079 topLeft, getPixelFloatPos(bitmap, .25f, .25f)); 1080 assertEquals("Top right", topRight, getPixelFloatPos(bitmap, .75f, .25f)); 1081 assertEquals("Bottom left", bottomLeft, getPixelFloatPos(bitmap, .25f, .75f)); 1082 assertEquals("Bottom right", bottomRight, getPixelFloatPos(bitmap, .75f, .75f)); 1083 1084 // and some closer to the center point, to ensure that our quadrants are even 1085 float below = .45f; 1086 float above = .55f; 1087 assertEquals("Top left II " + Integer.toHexString(topLeft) + ", actual= " 1088 + Integer.toHexString(getPixelFloatPos(bitmap, below, below)), 1089 topLeft, getPixelFloatPos(bitmap, below, below)); 1090 assertEquals("Top right II", topRight, getPixelFloatPos(bitmap, above, below)); 1091 assertEquals("Bottom left II", bottomLeft, getPixelFloatPos(bitmap, below, above)); 1092 assertEquals("Bottom right II", bottomRight, getPixelFloatPos(bitmap, above, above)); 1093 } catch (AssertionError err) { 1094 BitmapDumper.dumpBitmap(bitmap, testName, className); 1095 throw err; 1096 } 1097 } 1098 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight, int threshold)1099 private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 1100 int bottomLeft, int bottomRight, int threshold) { 1101 Function<Float, Integer> getX = (Float x) -> (int) (bitmap.getWidth() * x); 1102 Function<Float, Integer> getY = (Float y) -> (int) (bitmap.getHeight() * y); 1103 1104 // Just quickly sample 4 pixels in the various regions. 1105 assertBitmapColor("Top left", bitmap, topLeft, 1106 getX.apply(.25f), getY.apply(.25f), threshold); 1107 assertBitmapColor("Top right", bitmap, topRight, 1108 getX.apply(.75f), getY.apply(.25f), threshold); 1109 assertBitmapColor("Bottom left", bitmap, bottomLeft, 1110 getX.apply(.25f), getY.apply(.75f), threshold); 1111 assertBitmapColor("Bottom right", bitmap, bottomRight, 1112 getX.apply(.75f), getY.apply(.75f), threshold); 1113 1114 float below = .4f; 1115 float above = .6f; 1116 assertBitmapColor("Top left II", bitmap, topLeft, 1117 getX.apply(below), getY.apply(below), threshold); 1118 assertBitmapColor("Top right II", bitmap, topRight, 1119 getX.apply(above), getY.apply(below), threshold); 1120 assertBitmapColor("Bottom left II", bitmap, bottomLeft, 1121 getX.apply(below), getY.apply(above), threshold); 1122 assertBitmapColor("Bottom right II", bitmap, bottomRight, 1123 getX.apply(above), getY.apply(above), threshold); 1124 } 1125 assertBitmapEdgeColor(Bitmap bitmap, int edgeColor)1126 private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) { 1127 // Just quickly sample a few pixels on the edge and assert 1128 // they are edge color, then assert that just inside the edge is a different color 1129 assertBitmapColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 1); 1130 assertBitmapNotColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 2); 1131 1132 assertBitmapColor("Left edge", bitmap, edgeColor, 1, bitmap.getHeight() / 2); 1133 assertBitmapNotColor("Left edge", bitmap, edgeColor, 2, bitmap.getHeight() / 2); 1134 1135 assertBitmapColor("Bottom edge", bitmap, edgeColor, 1136 bitmap.getWidth() / 2, bitmap.getHeight() - 2); 1137 assertBitmapNotColor("Bottom edge", bitmap, edgeColor, 1138 bitmap.getWidth() / 2, bitmap.getHeight() - 3); 1139 1140 assertBitmapColor("Right edge", bitmap, edgeColor, 1141 bitmap.getWidth() - 2, bitmap.getHeight() / 2); 1142 assertBitmapNotColor("Right edge", bitmap, edgeColor, 1143 bitmap.getWidth() - 3, bitmap.getHeight() / 2); 1144 } 1145 pixelsAreSame(int ideal, int given, int threshold)1146 private static boolean pixelsAreSame(int ideal, int given, int threshold) { 1147 int error = Math.abs(Color.red(ideal) - Color.red(given)); 1148 error += Math.abs(Color.green(ideal) - Color.green(given)); 1149 error += Math.abs(Color.blue(ideal) - Color.blue(given)); 1150 return (error < threshold); 1151 } 1152 fail(Bitmap bitmap, String message)1153 private void fail(Bitmap bitmap, String message) { 1154 BitmapDumper.dumpBitmap(bitmap, mTestName.getMethodName(), "PixelCopyTest"); 1155 Assert.fail(message); 1156 } 1157 assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y)1158 private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y) { 1159 assertBitmapColor(debug, bitmap, color, x, y, 10); 1160 } 1161 assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y, int threshold)1162 private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y, 1163 int threshold) { 1164 int pixel = bitmap.getPixel(x, y); 1165 if (!pixelsAreSame(color, pixel, threshold)) { 1166 fail(bitmap, debug + "; expected=" + Integer.toHexString(color) + ", actual=" 1167 + Integer.toHexString(pixel)); 1168 } 1169 } 1170 assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y)1171 private void assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y) { 1172 int pixel = bitmap.getPixel(x, y); 1173 if (pixelsAreSame(color, pixel, 10)) { 1174 fail(bitmap, debug + "; actual=" + Integer.toHexString(pixel) 1175 + " shouldn't have matched " + Integer.toHexString(color)); 1176 } 1177 } 1178 1179 private static class SurfaceTextureRule implements TestRule { 1180 private SurfaceTexture mSurfaceTexture = null; 1181 private Surface mSurface = null; 1182 createIfNecessary()1183 private void createIfNecessary() { 1184 mSurfaceTexture = new SurfaceTexture(false); 1185 mSurface = new Surface(mSurfaceTexture); 1186 } 1187 getSurface()1188 public Surface getSurface() { 1189 createIfNecessary(); 1190 return mSurface; 1191 } 1192 1193 @Override apply(Statement base, Description description)1194 public Statement apply(Statement base, Description description) { 1195 return new CreateSurfaceTextureStatement(base); 1196 } 1197 1198 private class CreateSurfaceTextureStatement extends Statement { 1199 1200 private final Statement mBase; 1201 CreateSurfaceTextureStatement(Statement base)1202 public CreateSurfaceTextureStatement(Statement base) { 1203 mBase = base; 1204 } 1205 1206 @Override evaluate()1207 public void evaluate() throws Throwable { 1208 try { 1209 mBase.evaluate(); 1210 } finally { 1211 try { 1212 if (mSurface != null) mSurface.release(); 1213 } catch (Throwable t) {} 1214 try { 1215 if (mSurfaceTexture != null) mSurfaceTexture.release(); 1216 } catch (Throwable t) {} 1217 } 1218 } 1219 } 1220 } 1221 } 1222