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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.when; 26 27 import android.app.Instrumentation; 28 import android.content.Context; 29 import android.content.pm.ActivityInfo; 30 import android.graphics.Bitmap; 31 import android.graphics.Bitmap.Config; 32 import android.graphics.Color; 33 import android.graphics.ColorSpace; 34 import android.graphics.Rect; 35 import android.graphics.SurfaceTexture; 36 import android.os.Debug; 37 import android.os.Debug.MemoryInfo; 38 import android.util.Half; 39 import android.util.Log; 40 import android.view.PixelCopy; 41 import android.view.Surface; 42 import android.view.View; 43 import android.view.Window; 44 import android.view.WindowManager; 45 46 import androidx.test.InstrumentationRegistry; 47 import androidx.test.filters.LargeTest; 48 import androidx.test.filters.MediumTest; 49 import androidx.test.rule.ActivityTestRule; 50 import androidx.test.runner.AndroidJUnit4; 51 52 import com.android.compatibility.common.util.SynchronousPixelCopy; 53 54 import org.junit.Before; 55 import org.junit.Rule; 56 import org.junit.Test; 57 import org.junit.rules.TestRule; 58 import org.junit.runner.Description; 59 import org.junit.runner.RunWith; 60 import org.junit.runners.model.Statement; 61 62 import java.nio.ByteBuffer; 63 import java.nio.ByteOrder; 64 import java.util.concurrent.CountDownLatch; 65 import java.util.concurrent.TimeUnit; 66 67 @MediumTest 68 @RunWith(AndroidJUnit4.class) 69 public class PixelCopyTest { 70 private static final String TAG = "PixelCopyTests"; 71 72 @Rule 73 public ActivityTestRule<PixelCopyGLProducerCtsActivity> mGLSurfaceViewActivityRule = 74 new ActivityTestRule<>(PixelCopyGLProducerCtsActivity.class, false, false); 75 76 @Rule 77 public ActivityTestRule<PixelCopyVideoSourceActivity> mVideoSourceActivityRule = 78 new ActivityTestRule<>(PixelCopyVideoSourceActivity.class, false, false); 79 80 @Rule 81 public ActivityTestRule<PixelCopyViewProducerActivity> mWindowSourceActivityRule = 82 new ActivityTestRule<>(PixelCopyViewProducerActivity.class, false, false); 83 84 @Rule 85 public ActivityTestRule<PixelCopyWideGamutViewProducerActivity> 86 mWideGamutWindowSourceActivityRule = new ActivityTestRule<>( 87 PixelCopyWideGamutViewProducerActivity.class, false, false); 88 89 @Rule 90 public ActivityTestRule<PixelCopyViewProducerDialogActivity> mDialogSourceActivityRule = 91 new ActivityTestRule<>(PixelCopyViewProducerDialogActivity.class, false, false); 92 93 @Rule 94 public SurfaceTextureRule mSurfaceRule = new SurfaceTextureRule(); 95 96 private Instrumentation mInstrumentation; 97 private SynchronousPixelCopy mCopyHelper; 98 99 @Before setup()100 public void setup() { 101 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 102 assertNotNull(mInstrumentation); 103 mCopyHelper = new SynchronousPixelCopy(); 104 } 105 106 @Test(expected = IllegalArgumentException.class) testNullDest()107 public void testNullDest() { 108 Bitmap dest = null; 109 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 110 } 111 112 @Test(expected = IllegalArgumentException.class) testRecycledDest()113 public void testRecycledDest() { 114 Bitmap dest = Bitmap.createBitmap(5, 5, Config.ARGB_8888); 115 dest.recycle(); 116 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 117 } 118 119 @Test testNoSourceData()120 public void testNoSourceData() { 121 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 122 int result = mCopyHelper.request(mSurfaceRule.getSurface(), dest); 123 assertEquals(PixelCopy.ERROR_SOURCE_NO_DATA, result); 124 } 125 126 @Test(expected = IllegalArgumentException.class) testEmptySourceRectSurface()127 public void testEmptySourceRectSurface() { 128 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 129 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(), dest); 130 } 131 132 @Test(expected = IllegalArgumentException.class) testEmptySourceRectWindow()133 public void testEmptySourceRectWindow() { 134 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 135 mCopyHelper.request(mock(Window.class), new Rect(), dest); 136 } 137 138 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectSurface()139 public void testInvalidSourceRectSurface() { 140 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 141 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(10, 10, 0, 0), dest); 142 } 143 144 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectWindow()145 public void testInvalidSourceRectWindow() { 146 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 147 mCopyHelper.request(mock(Window.class), new Rect(10, 10, 0, 0), dest); 148 } 149 150 @Test(expected = IllegalArgumentException.class) testNoDecorView()151 public void testNoDecorView() { 152 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 153 Window mockWindow = mock(Window.class); 154 mCopyHelper.request(mockWindow, dest); 155 } 156 157 @Test(expected = IllegalArgumentException.class) testNoViewRoot()158 public void testNoViewRoot() { 159 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 160 Window mockWindow = mock(Window.class); 161 View view = new View(mInstrumentation.getTargetContext()); 162 when(mockWindow.peekDecorView()).thenReturn(view); 163 mCopyHelper.request(mockWindow, dest); 164 } 165 waitForGlProducerActivity()166 private PixelCopyGLProducerCtsActivity waitForGlProducerActivity() { 167 CountDownLatch swapFence = new CountDownLatch(2); 168 169 PixelCopyGLProducerCtsActivity activity = 170 mGLSurfaceViewActivityRule.launchActivity(null); 171 activity.setSwapFence(swapFence); 172 173 try { 174 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 175 activity.getView().requestRender(); 176 } 177 } catch (InterruptedException ex) { 178 fail("Interrupted, error=" + ex.getMessage()); 179 } 180 return activity; 181 } 182 183 @Test testGlProducerFullsize()184 public void testGlProducerFullsize() { 185 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 186 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 187 int result = mCopyHelper.request(activity.getView(), bitmap); 188 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 189 assertEquals(100, bitmap.getWidth()); 190 assertEquals(100, bitmap.getHeight()); 191 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 192 assertBitmapQuadColor(bitmap, 193 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 194 } 195 196 @Test testGlProducerCropTopLeft()197 public void testGlProducerCropTopLeft() { 198 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 199 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 200 int result = mCopyHelper.request(activity.getView(), new Rect(0, 0, 50, 50), bitmap); 201 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 202 assertBitmapQuadColor(bitmap, 203 Color.RED, Color.RED, Color.RED, Color.RED); 204 } 205 206 @Test testGlProducerCropCenter()207 public void testGlProducerCropCenter() { 208 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 209 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 210 int result = mCopyHelper.request(activity.getView(), new Rect(25, 25, 75, 75), bitmap); 211 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 212 assertBitmapQuadColor(bitmap, 213 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 214 } 215 216 @Test testGlProducerCropBottomHalf()217 public void testGlProducerCropBottomHalf() { 218 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 219 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 220 int result = mCopyHelper.request(activity.getView(), new Rect(0, 50, 100, 100), bitmap); 221 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 222 assertBitmapQuadColor(bitmap, 223 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 224 } 225 226 @Test testGlProducerCropClamping()227 public void testGlProducerCropClamping() { 228 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 229 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 230 int result = mCopyHelper.request(activity.getView(), new Rect(50, -50, 150, 50), bitmap); 231 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 232 assertBitmapQuadColor(bitmap, 233 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN); 234 } 235 236 @Test testGlProducerScaling()237 public void testGlProducerScaling() { 238 // Since we only sample mid-pixel of each qudrant, filtering 239 // quality isn't tested 240 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 241 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 242 int result = mCopyHelper.request(activity.getView(), bitmap); 243 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 244 // Make sure nothing messed with the bitmap 245 assertEquals(20, bitmap.getWidth()); 246 assertEquals(20, bitmap.getHeight()); 247 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 248 assertBitmapQuadColor(bitmap, 249 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 250 } 251 252 @Test testReuseBitmap()253 public void testReuseBitmap() { 254 // Since we only sample mid-pixel of each qudrant, filtering 255 // quality isn't tested 256 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 257 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 258 int result = mCopyHelper.request(activity.getView(), bitmap); 259 // Make sure nothing messed with the bitmap 260 assertEquals(20, bitmap.getWidth()); 261 assertEquals(20, bitmap.getHeight()); 262 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 263 assertBitmapQuadColor(bitmap, 264 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 265 int generationId = bitmap.getGenerationId(); 266 result = mCopyHelper.request(activity.getView(), bitmap); 267 // Make sure nothing messed with the bitmap 268 assertEquals(20, bitmap.getWidth()); 269 assertEquals(20, bitmap.getHeight()); 270 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 271 assertBitmapQuadColor(bitmap, 272 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 273 assertNotEquals(generationId, bitmap.getGenerationId()); 274 } 275 waitForWindowProducerActivity()276 private Window waitForWindowProducerActivity() { 277 PixelCopyViewProducerActivity activity = 278 mWindowSourceActivityRule.launchActivity(null); 279 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 280 return activity.getWindow(); 281 } 282 makeWindowRect(int left, int top, int right, int bottom)283 private Rect makeWindowRect(int left, int top, int right, int bottom) { 284 Rect r = new Rect(left, top, right, bottom); 285 mWindowSourceActivityRule.getActivity().normalizedToSurface(r); 286 return r; 287 } 288 289 @Test testWindowProducer()290 public void testWindowProducer() { 291 Bitmap bitmap; 292 Window window = waitForWindowProducerActivity(); 293 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 294 do { 295 Rect src = makeWindowRect(0, 0, 100, 100); 296 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 297 int result = mCopyHelper.request(window, src, bitmap); 298 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 299 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 300 assertBitmapQuadColor(bitmap, 301 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 302 assertBitmapEdgeColor(bitmap, Color.YELLOW); 303 } while (activity.rotate()); 304 } 305 306 @Test testWindowProducerCropTopLeft()307 public void testWindowProducerCropTopLeft() { 308 Window window = waitForWindowProducerActivity(); 309 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 310 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 311 do { 312 int result = mCopyHelper.request(window, makeWindowRect(0, 0, 50, 50), bitmap); 313 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 314 assertBitmapQuadColor(bitmap, 315 Color.RED, Color.RED, Color.RED, Color.RED); 316 } while (activity.rotate()); 317 } 318 319 @Test testWindowProducerCropCenter()320 public void testWindowProducerCropCenter() { 321 Window window = waitForWindowProducerActivity(); 322 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 323 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 324 do { 325 int result = mCopyHelper.request(window, makeWindowRect(25, 25, 75, 75), bitmap); 326 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 327 assertBitmapQuadColor(bitmap, 328 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 329 } while (activity.rotate()); 330 } 331 332 @Test testWindowProducerCropBottomHalf()333 public void testWindowProducerCropBottomHalf() { 334 Window window = waitForWindowProducerActivity(); 335 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 336 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 337 do { 338 int result = mCopyHelper.request(window, makeWindowRect(0, 50, 100, 100), bitmap); 339 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 340 assertBitmapQuadColor(bitmap, 341 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 342 } while (activity.rotate()); 343 } 344 345 @Test testWindowProducerScaling()346 public void testWindowProducerScaling() { 347 // Since we only sample mid-pixel of each qudrant, filtering 348 // quality isn't tested 349 Window window = waitForWindowProducerActivity(); 350 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 351 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 352 do { 353 int result = mCopyHelper.request(window, bitmap); 354 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 355 // Make sure nothing messed with the bitmap 356 assertEquals(20, bitmap.getWidth()); 357 assertEquals(20, bitmap.getHeight()); 358 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 359 assertBitmapQuadColor(bitmap, 360 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 361 } while (activity.rotate()); 362 } 363 364 @Test testWindowProducerCopyToRGBA16F()365 public void testWindowProducerCopyToRGBA16F() { 366 Window window = waitForWindowProducerActivity(); 367 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 368 369 Bitmap bitmap; 370 do { 371 Rect src = makeWindowRect(0, 0, 100, 100); 372 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16); 373 int result = mCopyHelper.request(window, src, bitmap); 374 // On OpenGL ES 2.0 devices a copy to RGBA_F16 can fail because there's 375 // not support for float textures 376 if (result != PixelCopy.ERROR_DESTINATION_INVALID) { 377 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 378 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 379 assertBitmapQuadColor(bitmap, 380 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 381 assertBitmapEdgeColor(bitmap, Color.YELLOW); 382 } 383 } while (activity.rotate()); 384 } 385 waitForWideGamutWindowProducerActivity()386 private Window waitForWideGamutWindowProducerActivity() { 387 PixelCopyWideGamutViewProducerActivity activity = 388 mWideGamutWindowSourceActivityRule.launchActivity(null); 389 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 390 return activity.getWindow(); 391 } 392 makeWideGamutWindowRect(int left, int top, int right, int bottom)393 private Rect makeWideGamutWindowRect(int left, int top, int right, int bottom) { 394 Rect r = new Rect(left, top, right, bottom); 395 mWideGamutWindowSourceActivityRule.getActivity().offsetForContent(r); 396 return r; 397 } 398 399 @Test testWideGamutWindowProducerCopyToRGBA8888()400 public void testWideGamutWindowProducerCopyToRGBA8888() { 401 Window window = waitForWideGamutWindowProducerActivity(); 402 assertEquals( 403 ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT, window.getAttributes().getColorMode()); 404 405 // Early out if the device does not support wide color gamut rendering 406 if (!window.isWideColorGamut()) { 407 return; 408 } 409 410 PixelCopyWideGamutViewProducerActivity activity = 411 mWideGamutWindowSourceActivityRule.getActivity(); 412 413 Bitmap bitmap; 414 do { 415 Rect src = makeWideGamutWindowRect(0, 0, 128, 128); 416 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 417 int result = mCopyHelper.request(window, src, bitmap); 418 419 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 420 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 421 422 assertEquals("Top left", Color.RED, bitmap.getPixel(32, 32)); 423 assertEquals("Top right", Color.GREEN, bitmap.getPixel(96, 32)); 424 assertEquals("Bottom left", Color.BLUE, bitmap.getPixel(32, 96)); 425 assertEquals("Bottom right", Color.YELLOW, bitmap.getPixel(96, 96)); 426 } while (activity.rotate()); 427 } 428 429 @Test testWideGamutWindowProducerCopyToRGBA16F()430 public void testWideGamutWindowProducerCopyToRGBA16F() { 431 Window window = waitForWideGamutWindowProducerActivity(); 432 assertEquals( 433 ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT, window.getAttributes().getColorMode()); 434 435 // Early out if the device does not support wide color gamut rendering 436 if (!window.isWideColorGamut()) { 437 return; 438 } 439 440 PixelCopyWideGamutViewProducerActivity activity = 441 mWideGamutWindowSourceActivityRule.getActivity(); 442 final WindowManager windowManager = (WindowManager) activity.getSystemService( 443 Context.WINDOW_SERVICE); 444 final ColorSpace colorSpace = windowManager.getDefaultDisplay() 445 .getPreferredWideGamutColorSpace(); 446 final ColorSpace.Connector proPhotoToDisplayWideColorSpace = ColorSpace.connect( 447 ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), colorSpace); 448 final ColorSpace.Connector displayWideColorSpaceToExtendedSrgb = ColorSpace.connect( 449 colorSpace, ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); 450 451 final float[] intermediateRed = proPhotoToDisplayWideColorSpace.transform(1.0f, 0.0f, 0.0f); 452 final float[] intermediateGreen = proPhotoToDisplayWideColorSpace 453 .transform(0.0f, 1.0f, 0.0f); 454 final float[] intermediateBlue = proPhotoToDisplayWideColorSpace 455 .transform(0.0f, 0.0f, 1.0f); 456 final float[] intermediateYellow = proPhotoToDisplayWideColorSpace 457 .transform(1.0f, 1.0f, 0.0f); 458 459 final float[] expectedRed = displayWideColorSpaceToExtendedSrgb.transform(intermediateRed); 460 final float[] expectedGreen = displayWideColorSpaceToExtendedSrgb 461 .transform(intermediateGreen); 462 final float[] expectedBlue = displayWideColorSpaceToExtendedSrgb 463 .transform(intermediateBlue); 464 final float[] expectedYellow = displayWideColorSpaceToExtendedSrgb 465 .transform(intermediateYellow); 466 467 Bitmap bitmap; 468 int i = 0; 469 do { 470 Rect src = makeWideGamutWindowRect(0, 0, 128, 128); 471 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16, true, 472 ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB)); 473 int result = mCopyHelper.request(window, src, bitmap); 474 475 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 476 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 477 478 ByteBuffer dst = ByteBuffer.allocateDirect(bitmap.getAllocationByteCount()); 479 bitmap.copyPixelsToBuffer(dst); 480 dst.rewind(); 481 dst.order(ByteOrder.LITTLE_ENDIAN); 482 483 // ProPhoto RGB red in scRGB-nl 484 assertEqualsRgba16f("Top left", bitmap, 32, 32, dst, expectedRed[0], 485 expectedRed[1], expectedRed[2], 1.0f); 486 // ProPhoto RGB green in scRGB-nl 487 assertEqualsRgba16f("Top right", bitmap, 96, 32, dst, expectedGreen[0], 488 expectedGreen[1], expectedGreen[2], 1.0f); 489 // ProPhoto RGB blue in scRGB-nl 490 assertEqualsRgba16f("Bottom left", bitmap, 32, 96, dst, expectedBlue[0], 491 expectedBlue[1], expectedBlue[2], 1.0f); 492 // ProPhoto RGB yellow in scRGB-nl 493 assertEqualsRgba16f("Bottom right", bitmap, 96, 96, dst, expectedYellow[0], 494 expectedYellow[1], expectedYellow[2], 1.0f); 495 } while (activity.rotate()); 496 } 497 waitForDialogProducerActivity()498 private Window waitForDialogProducerActivity() { 499 PixelCopyViewProducerActivity activity = 500 mDialogSourceActivityRule.launchActivity(null); 501 activity.waitForFirstDrawCompleted(10, TimeUnit.SECONDS); 502 return activity.getWindow(); 503 } 504 makeDialogRect(int left, int top, int right, int bottom)505 private Rect makeDialogRect(int left, int top, int right, int bottom) { 506 Rect r = new Rect(left, top, right, bottom); 507 mDialogSourceActivityRule.getActivity().normalizedToSurface(r); 508 return r; 509 } 510 511 @Test testDialogProducer()512 public void testDialogProducer() { 513 Bitmap bitmap; 514 Window window = waitForDialogProducerActivity(); 515 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 516 do { 517 Rect src = makeDialogRect(0, 0, 100, 100); 518 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 519 int result = mCopyHelper.request(window, src, bitmap); 520 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 521 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 522 assertBitmapQuadColor(bitmap, 523 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 524 assertBitmapEdgeColor(bitmap, Color.YELLOW); 525 } while (activity.rotate()); 526 } 527 528 @Test testDialogProducerCropTopLeft()529 public void testDialogProducerCropTopLeft() { 530 Window window = waitForDialogProducerActivity(); 531 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 532 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 533 do { 534 int result = mCopyHelper.request(window, makeDialogRect(0, 0, 50, 50), bitmap); 535 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 536 assertBitmapQuadColor(bitmap, 537 Color.RED, Color.RED, Color.RED, Color.RED); 538 } while (activity.rotate()); 539 } 540 541 @Test testDialogProducerCropCenter()542 public void testDialogProducerCropCenter() { 543 Window window = waitForDialogProducerActivity(); 544 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 545 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 546 do { 547 int result = mCopyHelper.request(window, makeDialogRect(25, 25, 75, 75), bitmap); 548 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 549 assertBitmapQuadColor(bitmap, 550 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 551 } while (activity.rotate()); 552 } 553 554 @Test testDialogProducerCropBottomHalf()555 public void testDialogProducerCropBottomHalf() { 556 Window window = waitForDialogProducerActivity(); 557 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 558 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 559 do { 560 int result = mCopyHelper.request(window, makeDialogRect(0, 50, 100, 100), bitmap); 561 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 562 assertBitmapQuadColor(bitmap, 563 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 564 } while (activity.rotate()); 565 } 566 567 @Test testDialogProducerScaling()568 public void testDialogProducerScaling() { 569 // Since we only sample mid-pixel of each qudrant, filtering 570 // quality isn't tested 571 Window window = waitForDialogProducerActivity(); 572 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 573 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 574 do { 575 int result = mCopyHelper.request(window, bitmap); 576 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 577 // Make sure nothing messed with the bitmap 578 assertEquals(20, bitmap.getWidth()); 579 assertEquals(20, bitmap.getHeight()); 580 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 581 assertBitmapQuadColor(bitmap, 582 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 583 } while (activity.rotate()); 584 } 585 586 @Test testDialogProducerCopyToRGBA16F()587 public void testDialogProducerCopyToRGBA16F() { 588 Window window = waitForDialogProducerActivity(); 589 PixelCopyViewProducerActivity activity = mDialogSourceActivityRule.getActivity(); 590 591 Bitmap bitmap; 592 do { 593 Rect src = makeDialogRect(0, 0, 100, 100); 594 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.RGBA_F16); 595 int result = mCopyHelper.request(window, src, bitmap); 596 // On OpenGL ES 2.0 devices a copy to RGBA_F16 can fail because there's 597 // not support for float textures 598 if (result != PixelCopy.ERROR_DESTINATION_INVALID) { 599 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 600 assertEquals(Config.RGBA_F16, bitmap.getConfig()); 601 assertBitmapQuadColor(bitmap, 602 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 603 assertBitmapEdgeColor(bitmap, Color.YELLOW); 604 } 605 } while (activity.rotate()); 606 } 607 assertEqualsRgba16f(String message, Bitmap bitmap, int x, int y, ByteBuffer dst, float r, float g, float b, float a)608 private static void assertEqualsRgba16f(String message, Bitmap bitmap, int x, int y, 609 ByteBuffer dst, float r, float g, float b, float a) { 610 int index = y * bitmap.getRowBytes() + (x << 3); 611 short cR = dst.getShort(index); 612 short cG = dst.getShort(index + 2); 613 short cB = dst.getShort(index + 4); 614 short cA = dst.getShort(index + 6); 615 616 assertEquals(message, r, Half.toFloat(cR), 0.01); 617 assertEquals(message, g, Half.toFloat(cG), 0.01); 618 assertEquals(message, b, Half.toFloat(cB), 0.01); 619 assertEquals(message, a, Half.toFloat(cA), 0.01); 620 } 621 runGcAndFinalizersSync()622 private void runGcAndFinalizersSync() { 623 final CountDownLatch fence = new CountDownLatch(1); 624 new Object() { 625 @Override 626 protected void finalize() throws Throwable { 627 try { 628 fence.countDown(); 629 } finally { 630 super.finalize(); 631 } 632 } 633 }; 634 try { 635 do { 636 Runtime.getRuntime().gc(); 637 Runtime.getRuntime().runFinalization(); 638 } while (!fence.await(100, TimeUnit.MILLISECONDS)); 639 } catch (InterruptedException ex) { 640 throw new RuntimeException(ex); 641 } 642 Runtime.getRuntime().gc(); 643 } 644 assertNotLeaking(int iteration, MemoryInfo start, MemoryInfo end)645 private void assertNotLeaking(int iteration, MemoryInfo start, MemoryInfo end) { 646 Debug.getMemoryInfo(end); 647 if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) { 648 runGcAndFinalizersSync(); 649 Debug.getMemoryInfo(end); 650 if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) { 651 // Guarded by if so we don't continually generate garbage for the 652 // assertion string. 653 assertEquals("Memory leaked, iteration=" + iteration, 654 start.getTotalPss(), end.getTotalPss(), 655 2000 /* kb */); 656 } 657 } 658 } 659 660 @Test 661 @LargeTest testNotLeaking()662 public void testNotLeaking() { 663 try { 664 CountDownLatch swapFence = new CountDownLatch(2); 665 666 PixelCopyGLProducerCtsActivity activity = 667 mGLSurfaceViewActivityRule.launchActivity(null); 668 activity.setSwapFence(swapFence); 669 670 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 671 activity.getView().requestRender(); 672 } 673 674 // Test a fullsize copy 675 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 676 677 MemoryInfo meminfoStart = new MemoryInfo(); 678 MemoryInfo meminfoEnd = new MemoryInfo(); 679 680 for (int i = 0; i < 1000; i++) { 681 if (i == 2) { 682 // Not really the "start" but by having done a couple 683 // we've fully initialized any state that may be required, 684 // so memory usage should be stable now 685 runGcAndFinalizersSync(); 686 Debug.getMemoryInfo(meminfoStart); 687 } 688 if (i % 100 == 5) { 689 assertNotLeaking(i, meminfoStart, meminfoEnd); 690 } 691 int result = mCopyHelper.request(activity.getView(), bitmap); 692 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 693 // Make sure nothing messed with the bitmap 694 assertEquals(100, bitmap.getWidth()); 695 assertEquals(100, bitmap.getHeight()); 696 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 697 assertBitmapQuadColor(bitmap, 698 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 699 } 700 701 assertNotLeaking(1000, meminfoStart, meminfoEnd); 702 703 } catch (InterruptedException e) { 704 fail("Interrupted, error=" + e.getMessage()); 705 } 706 } 707 708 @Test testVideoProducer()709 public void testVideoProducer() throws InterruptedException { 710 PixelCopyVideoSourceActivity activity = 711 mVideoSourceActivityRule.launchActivity(null); 712 if (!activity.canPlayVideo()) { 713 Log.i(TAG, "Skipping testVideoProducer, video codec isn't supported"); 714 return; 715 } 716 // This returns when the video has been prepared and playback has 717 // been started, it doesn't necessarily means a frame has actually been 718 // produced. There sadly isn't a callback for that. 719 // So we'll try for up to 900ms after this event to acquire a frame, otherwise 720 // it's considered a timeout. 721 activity.waitForPlaying(); 722 assertTrue("Failed to start video playback", activity.canPlayVideo()); 723 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 724 int copyResult = PixelCopy.ERROR_SOURCE_NO_DATA; 725 for (int i = 0; i < 30; i++) { 726 copyResult = mCopyHelper.request(activity.getVideoView(), bitmap); 727 if (copyResult != PixelCopy.ERROR_SOURCE_NO_DATA) { 728 break; 729 } 730 Thread.sleep(30); 731 } 732 assertEquals(PixelCopy.SUCCESS, copyResult); 733 // A large threshold is used because decoder accuracy is covered in the 734 // media CTS tests, so we are mainly interested in verifying that rotation 735 // and YUV->RGB conversion were handled properly. 736 assertBitmapQuadColor(bitmap, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 737 738 // Test that cropping works. 739 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 0, 50, 50), bitmap); 740 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 741 assertBitmapQuadColor(bitmap, 742 Color.RED, Color.RED, Color.RED, Color.RED, 30); 743 744 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(25, 25, 75, 75), bitmap); 745 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 746 assertBitmapQuadColor(bitmap, 747 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 748 749 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 50, 100, 100), bitmap); 750 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 751 assertBitmapQuadColor(bitmap, 752 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK, 30); 753 754 // Test that clamping works 755 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(50, -50, 150, 50), bitmap); 756 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 757 assertBitmapQuadColor(bitmap, 758 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, 30); 759 } 760 getPixelFloatPos(Bitmap bitmap, float xpos, float ypos)761 private static int getPixelFloatPos(Bitmap bitmap, float xpos, float ypos) { 762 return bitmap.getPixel((int) (bitmap.getWidth() * xpos), (int) (bitmap.getHeight() * ypos)); 763 } 764 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight)765 public static void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 766 int bottomLeft, int bottomRight) { 767 // Just quickly sample 4 pixels in the various regions. 768 assertEquals("Top left " + Integer.toHexString(topLeft) + ", actual= " 769 + Integer.toHexString(getPixelFloatPos(bitmap, .25f, .25f)), 770 topLeft, getPixelFloatPos(bitmap, .25f, .25f)); 771 assertEquals("Top right", topRight, getPixelFloatPos(bitmap, .75f, .25f)); 772 assertEquals("Bottom left", bottomLeft, getPixelFloatPos(bitmap, .25f, .75f)); 773 assertEquals("Bottom right", bottomRight, getPixelFloatPos(bitmap, .75f, .75f)); 774 } 775 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight, int threshold)776 private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 777 int bottomLeft, int bottomRight, int threshold) { 778 // Just quickly sample 4 pixels in the various regions. 779 assertTrue("Top left", pixelsAreSame(topLeft, getPixelFloatPos(bitmap, .25f, .25f), 780 threshold)); 781 assertTrue("Top right", pixelsAreSame(topRight, getPixelFloatPos(bitmap, .75f, .25f), 782 threshold)); 783 assertTrue("Bottom left", pixelsAreSame(bottomLeft, getPixelFloatPos(bitmap, .25f, .75f), 784 threshold)); 785 assertTrue("Bottom right", pixelsAreSame(bottomRight, getPixelFloatPos(bitmap, .75f, .75f), 786 threshold)); 787 } 788 assertBitmapEdgeColor(Bitmap bitmap, int edgeColor)789 private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) { 790 // Just quickly sample a few pixels on the edge and assert 791 // they are edge color, then assert that just inside the edge is a different color 792 assertBitmapColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 1); 793 assertBitmapNotColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 2); 794 795 assertBitmapColor("Left edge", bitmap, edgeColor, 1, bitmap.getHeight() / 2); 796 assertBitmapNotColor("Left edge", bitmap, edgeColor, 2, bitmap.getHeight() / 2); 797 798 assertBitmapColor("Bottom edge", bitmap, edgeColor, 799 bitmap.getWidth() / 2, bitmap.getHeight() - 2); 800 assertBitmapNotColor("Bottom edge", bitmap, edgeColor, 801 bitmap.getWidth() / 2, bitmap.getHeight() - 3); 802 803 assertBitmapColor("Right edge", bitmap, edgeColor, 804 bitmap.getWidth() - 2, bitmap.getHeight() / 2); 805 assertBitmapNotColor("Right edge", bitmap, edgeColor, 806 bitmap.getWidth() - 3, bitmap.getHeight() / 2); 807 } 808 pixelsAreSame(int ideal, int given, int threshold)809 private boolean pixelsAreSame(int ideal, int given, int threshold) { 810 int error = Math.abs(Color.red(ideal) - Color.red(given)); 811 error += Math.abs(Color.green(ideal) - Color.green(given)); 812 error += Math.abs(Color.blue(ideal) - Color.blue(given)); 813 return (error < threshold); 814 } 815 assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y)816 private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y) { 817 int pixel = bitmap.getPixel(x, y); 818 if (!pixelsAreSame(color, pixel, 10)) { 819 fail(debug + "; expected=" + Integer.toHexString(color) + ", actual=" 820 + Integer.toHexString(pixel)); 821 } 822 } 823 assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y)824 private void assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y) { 825 int pixel = bitmap.getPixel(x, y); 826 if (pixelsAreSame(color, pixel, 10)) { 827 fail(debug + "; actual=" + Integer.toHexString(pixel) 828 + " shouldn't have matched " + Integer.toHexString(color)); 829 } 830 } 831 832 private static class SurfaceTextureRule implements TestRule { 833 private SurfaceTexture mSurfaceTexture = null; 834 private Surface mSurface = null; 835 createIfNecessary()836 private void createIfNecessary() { 837 mSurfaceTexture = new SurfaceTexture(false); 838 mSurface = new Surface(mSurfaceTexture); 839 } 840 getSurface()841 public Surface getSurface() { 842 createIfNecessary(); 843 return mSurface; 844 } 845 846 @Override apply(Statement base, Description description)847 public Statement apply(Statement base, Description description) { 848 return new CreateSurfaceTextureStatement(base); 849 } 850 851 private class CreateSurfaceTextureStatement extends Statement { 852 853 private final Statement mBase; 854 CreateSurfaceTextureStatement(Statement base)855 public CreateSurfaceTextureStatement(Statement base) { 856 mBase = base; 857 } 858 859 @Override evaluate()860 public void evaluate() throws Throwable { 861 try { 862 mBase.evaluate(); 863 } finally { 864 try { 865 if (mSurface != null) mSurface.release(); 866 } catch (Throwable t) {} 867 try { 868 if (mSurfaceTexture != null) mSurfaceTexture.release(); 869 } catch (Throwable t) {} 870 } 871 } 872 } 873 } 874 } 875