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