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