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.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 import android.app.Instrumentation; 27 import android.graphics.Bitmap; 28 import android.graphics.Bitmap.Config; 29 import android.graphics.Color; 30 import android.graphics.Rect; 31 import android.graphics.SurfaceTexture; 32 import android.os.Debug; 33 import android.os.Debug.MemoryInfo; 34 import android.support.test.InstrumentationRegistry; 35 import android.support.test.filters.LargeTest; 36 import android.support.test.filters.MediumTest; 37 import android.support.test.rule.ActivityTestRule; 38 import android.support.test.runner.AndroidJUnit4; 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 45 import com.android.compatibility.common.util.SynchronousPixelCopy; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.rules.TestRule; 51 import org.junit.runner.Description; 52 import org.junit.runner.RunWith; 53 import org.junit.runners.model.Statement; 54 55 import java.util.concurrent.CountDownLatch; 56 import java.util.concurrent.TimeUnit; 57 58 @MediumTest 59 @RunWith(AndroidJUnit4.class) 60 public class PixelCopyTest { 61 private static final String TAG = "PixelCopyTests"; 62 63 @Rule 64 public ActivityTestRule<PixelCopyGLProducerCtsActivity> mGLSurfaceViewActivityRule = 65 new ActivityTestRule<>(PixelCopyGLProducerCtsActivity.class, false, false); 66 67 @Rule 68 public ActivityTestRule<PixelCopyVideoSourceActivity> mVideoSourceActivityRule = 69 new ActivityTestRule<>(PixelCopyVideoSourceActivity.class, false, false); 70 71 @Rule 72 public ActivityTestRule<PixelCopyViewProducerActivity> mWindowSourceActivityRule = 73 new ActivityTestRule<>(PixelCopyViewProducerActivity.class, false, false); 74 75 @Rule 76 public SurfaceTextureRule mSurfaceRule = new SurfaceTextureRule(); 77 78 private Instrumentation mInstrumentation; 79 private SynchronousPixelCopy mCopyHelper; 80 81 @Before setup()82 public void setup() { 83 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 84 assertNotNull(mInstrumentation); 85 mCopyHelper = new SynchronousPixelCopy(); 86 } 87 88 @Test(expected = IllegalArgumentException.class) testNullDest()89 public void testNullDest() { 90 Bitmap dest = null; 91 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 92 } 93 94 @Test(expected = IllegalArgumentException.class) testRecycledDest()95 public void testRecycledDest() { 96 Bitmap dest = Bitmap.createBitmap(5, 5, Config.ARGB_8888); 97 dest.recycle(); 98 mCopyHelper.request(mSurfaceRule.getSurface(), dest); 99 } 100 101 @Test testNoSourceData()102 public void testNoSourceData() { 103 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 104 int result = mCopyHelper.request(mSurfaceRule.getSurface(), dest); 105 assertEquals(PixelCopy.ERROR_SOURCE_NO_DATA, result); 106 } 107 108 @Test(expected = IllegalArgumentException.class) testEmptySourceRectSurface()109 public void testEmptySourceRectSurface() { 110 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 111 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(), dest); 112 } 113 114 @Test(expected = IllegalArgumentException.class) testEmptySourceRectWindow()115 public void testEmptySourceRectWindow() { 116 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 117 mCopyHelper.request(mock(Window.class), new Rect(), dest); 118 } 119 120 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectSurface()121 public void testInvalidSourceRectSurface() { 122 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 123 mCopyHelper.request(mSurfaceRule.getSurface(), new Rect(10, 10, 0, 0), dest); 124 } 125 126 @Test(expected = IllegalArgumentException.class) testInvalidSourceRectWindow()127 public void testInvalidSourceRectWindow() { 128 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 129 mCopyHelper.request(mock(Window.class), new Rect(10, 10, 0, 0), dest); 130 } 131 132 @Test(expected = IllegalArgumentException.class) testNoDecorView()133 public void testNoDecorView() { 134 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 135 Window mockWindow = mock(Window.class); 136 mCopyHelper.request(mockWindow, dest); 137 } 138 139 @Test(expected = IllegalArgumentException.class) testNoViewRoot()140 public void testNoViewRoot() { 141 Bitmap dest = Bitmap.createBitmap(5, 5, Bitmap.Config.ARGB_8888); 142 Window mockWindow = mock(Window.class); 143 View view = new View(mInstrumentation.getTargetContext()); 144 when(mockWindow.peekDecorView()).thenReturn(view); 145 mCopyHelper.request(mockWindow, dest); 146 } 147 waitForGlProducerActivity()148 private PixelCopyGLProducerCtsActivity waitForGlProducerActivity() { 149 CountDownLatch swapFence = new CountDownLatch(2); 150 151 PixelCopyGLProducerCtsActivity activity = 152 mGLSurfaceViewActivityRule.launchActivity(null); 153 activity.setSwapFence(swapFence); 154 155 try { 156 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 157 activity.getView().requestRender(); 158 } 159 } catch (InterruptedException ex) { 160 fail("Interrupted, error=" + ex.getMessage()); 161 } 162 return activity; 163 } 164 165 @Test testGlProducerFullsize()166 public void testGlProducerFullsize() { 167 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 168 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 169 int result = mCopyHelper.request(activity.getView(), bitmap); 170 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 171 assertEquals(100, bitmap.getWidth()); 172 assertEquals(100, bitmap.getHeight()); 173 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 174 assertBitmapQuadColor(bitmap, 175 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 176 } 177 178 @Test testGlProducerCropTopLeft()179 public void testGlProducerCropTopLeft() { 180 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 181 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 182 int result = mCopyHelper.request(activity.getView(), new Rect(0, 0, 50, 50), bitmap); 183 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 184 assertBitmapQuadColor(bitmap, 185 Color.RED, Color.RED, Color.RED, Color.RED); 186 } 187 188 @Test testGlProducerCropCenter()189 public void testGlProducerCropCenter() { 190 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 191 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 192 int result = mCopyHelper.request(activity.getView(), new Rect(25, 25, 75, 75), bitmap); 193 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 194 assertBitmapQuadColor(bitmap, 195 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 196 } 197 198 @Test testGlProducerCropBottomHalf()199 public void testGlProducerCropBottomHalf() { 200 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 201 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 202 int result = mCopyHelper.request(activity.getView(), new Rect(0, 50, 100, 100), bitmap); 203 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 204 assertBitmapQuadColor(bitmap, 205 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 206 } 207 208 @Test testGlProducerCropClamping()209 public void testGlProducerCropClamping() { 210 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 211 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 212 int result = mCopyHelper.request(activity.getView(), new Rect(50, -50, 150, 50), bitmap); 213 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 214 assertBitmapQuadColor(bitmap, 215 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN); 216 } 217 218 @Test testGlProducerScaling()219 public void testGlProducerScaling() { 220 // Since we only sample mid-pixel of each qudrant, filtering 221 // quality isn't tested 222 PixelCopyGLProducerCtsActivity activity = waitForGlProducerActivity(); 223 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 224 int result = mCopyHelper.request(activity.getView(), bitmap); 225 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 226 // Make sure nothing messed with the bitmap 227 assertEquals(20, bitmap.getWidth()); 228 assertEquals(20, bitmap.getHeight()); 229 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 230 assertBitmapQuadColor(bitmap, 231 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 232 } 233 waitForWindowProducerActivity()234 private Window waitForWindowProducerActivity() { 235 PixelCopyViewProducerActivity activity = 236 mWindowSourceActivityRule.launchActivity(null); 237 activity.waitForFirstDrawCompleted(3, TimeUnit.SECONDS); 238 return activity.getWindow(); 239 } 240 makeWindowRect(int left, int top, int right, int bottom)241 private Rect makeWindowRect(int left, int top, int right, int bottom) { 242 Rect r = new Rect(left, top, right, bottom); 243 mWindowSourceActivityRule.getActivity().normalizedToSurface(r); 244 return r; 245 } 246 247 @Test testWindowProducer()248 public void testWindowProducer() { 249 Bitmap bitmap; 250 Window window = waitForWindowProducerActivity(); 251 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 252 do { 253 Rect src = makeWindowRect(0, 0, 100, 100); 254 bitmap = Bitmap.createBitmap(src.width(), src.height(), Config.ARGB_8888); 255 int result = mCopyHelper.request(window, src, bitmap); 256 assertEquals("Fullsize copy request failed", PixelCopy.SUCCESS, result); 257 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 258 assertBitmapQuadColor(bitmap, 259 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 260 assertBitmapEdgeColor(bitmap, Color.YELLOW); 261 } while (activity.rotate()); 262 } 263 264 @Test testWindowProducerCropTopLeft()265 public void testWindowProducerCropTopLeft() { 266 Window window = waitForWindowProducerActivity(); 267 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 268 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 269 do { 270 int result = mCopyHelper.request(window, makeWindowRect(0, 0, 50, 50), bitmap); 271 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 272 assertBitmapQuadColor(bitmap, 273 Color.RED, Color.RED, Color.RED, Color.RED); 274 } while (activity.rotate()); 275 } 276 277 @Test testWindowProducerCropCenter()278 public void testWindowProducerCropCenter() { 279 Window window = waitForWindowProducerActivity(); 280 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 281 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 282 do { 283 int result = mCopyHelper.request(window, makeWindowRect(25, 25, 75, 75), bitmap); 284 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 285 assertBitmapQuadColor(bitmap, 286 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 287 } while (activity.rotate()); 288 } 289 290 @Test testWindowProducerCropBottomHalf()291 public void testWindowProducerCropBottomHalf() { 292 Window window = waitForWindowProducerActivity(); 293 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 294 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 295 do { 296 int result = mCopyHelper.request(window, makeWindowRect(0, 50, 100, 100), bitmap); 297 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 298 assertBitmapQuadColor(bitmap, 299 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK); 300 } while (activity.rotate()); 301 } 302 303 @Test testWindowProducerScaling()304 public void testWindowProducerScaling() { 305 // Since we only sample mid-pixel of each qudrant, filtering 306 // quality isn't tested 307 Window window = waitForWindowProducerActivity(); 308 Bitmap bitmap = Bitmap.createBitmap(20, 20, Config.ARGB_8888); 309 PixelCopyViewProducerActivity activity = mWindowSourceActivityRule.getActivity(); 310 do { 311 int result = mCopyHelper.request(window, bitmap); 312 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, result); 313 // Make sure nothing messed with the bitmap 314 assertEquals(20, bitmap.getWidth()); 315 assertEquals(20, bitmap.getHeight()); 316 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 317 assertBitmapQuadColor(bitmap, 318 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 319 } while (activity.rotate()); 320 } 321 runGcAndFinalizersSync()322 private void runGcAndFinalizersSync() { 323 final CountDownLatch fence = new CountDownLatch(1); 324 new Object() { 325 @Override 326 protected void finalize() throws Throwable { 327 try { 328 fence.countDown(); 329 } finally { 330 super.finalize(); 331 } 332 } 333 }; 334 try { 335 do { 336 Runtime.getRuntime().gc(); 337 Runtime.getRuntime().runFinalization(); 338 } while (!fence.await(100, TimeUnit.MILLISECONDS)); 339 } catch (InterruptedException ex) { 340 throw new RuntimeException(ex); 341 } 342 Runtime.getRuntime().gc(); 343 } 344 assertNotLeaking(int iteration, MemoryInfo start, MemoryInfo end)345 private void assertNotLeaking(int iteration, MemoryInfo start, MemoryInfo end) { 346 Debug.getMemoryInfo(end); 347 if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) { 348 runGcAndFinalizersSync(); 349 Debug.getMemoryInfo(end); 350 if (end.getTotalPss() - start.getTotalPss() > 2000 /* kB */) { 351 // Guarded by if so we don't continually generate garbage for the 352 // assertion string. 353 assertEquals("Memory leaked, iteration=" + iteration, 354 start.getTotalPss(), end.getTotalPss(), 355 2000 /* kb */); 356 } 357 } 358 } 359 360 @Test 361 @LargeTest testNotLeaking()362 public void testNotLeaking() { 363 try { 364 CountDownLatch swapFence = new CountDownLatch(2); 365 366 PixelCopyGLProducerCtsActivity activity = 367 mGLSurfaceViewActivityRule.launchActivity(null); 368 activity.setSwapFence(swapFence); 369 370 while (!swapFence.await(5, TimeUnit.MILLISECONDS)) { 371 activity.getView().requestRender(); 372 } 373 374 // Test a fullsize copy 375 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 376 377 MemoryInfo meminfoStart = new MemoryInfo(); 378 MemoryInfo meminfoEnd = new MemoryInfo(); 379 380 for (int i = 0; i < 1000; i++) { 381 if (i == 2) { 382 // Not really the "start" but by having done a couple 383 // we've fully initialized any state that may be required, 384 // so memory usage should be stable now 385 runGcAndFinalizersSync(); 386 Debug.getMemoryInfo(meminfoStart); 387 } 388 if (i % 100 == 5) { 389 assertNotLeaking(i, meminfoStart, meminfoEnd); 390 } 391 int result = mCopyHelper.request(activity.getView(), bitmap); 392 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 393 // Make sure nothing messed with the bitmap 394 assertEquals(100, bitmap.getWidth()); 395 assertEquals(100, bitmap.getHeight()); 396 assertEquals(Config.ARGB_8888, bitmap.getConfig()); 397 assertBitmapQuadColor(bitmap, 398 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 399 } 400 401 assertNotLeaking(1000, meminfoStart, meminfoEnd); 402 403 } catch (InterruptedException e) { 404 fail("Interrupted, error=" + e.getMessage()); 405 } 406 } 407 408 @Test testVideoProducer()409 public void testVideoProducer() throws InterruptedException { 410 PixelCopyVideoSourceActivity activity = 411 mVideoSourceActivityRule.launchActivity(null); 412 if (!activity.canPlayVideo()) { 413 Log.i(TAG, "Skipping testVideoProducer, video codec isn't supported"); 414 return; 415 } 416 // This returns when the video has been prepared and playback has 417 // been started, it doesn't necessarily means a frame has actually been 418 // produced. There sadly isn't a callback for that. 419 // So we'll try for up to 900ms after this event to acquire a frame, otherwise 420 // it's considered a timeout. 421 activity.waitForPlaying(); 422 assertTrue("Failed to start video playback", activity.canPlayVideo()); 423 Bitmap bitmap = Bitmap.createBitmap(100, 100, Config.ARGB_8888); 424 int copyResult = PixelCopy.ERROR_SOURCE_NO_DATA; 425 for (int i = 0; i < 30; i++) { 426 copyResult = mCopyHelper.request(activity.getVideoView(), bitmap); 427 if (copyResult != PixelCopy.ERROR_SOURCE_NO_DATA) { 428 break; 429 } 430 Thread.sleep(30); 431 } 432 assertEquals(PixelCopy.SUCCESS, copyResult); 433 // A large threshold is used because decoder accuracy is covered in the 434 // media CTS tests, so we are mainly interested in verifying that rotation 435 // and YUV->RGB conversion were handled properly. 436 assertBitmapQuadColor(bitmap, Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 437 438 // Test that cropping works. 439 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 0, 50, 50), bitmap); 440 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 441 assertBitmapQuadColor(bitmap, 442 Color.RED, Color.RED, Color.RED, Color.RED, 30); 443 444 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(25, 25, 75, 75), bitmap); 445 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 446 assertBitmapQuadColor(bitmap, 447 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK, 30); 448 449 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(0, 50, 100, 100), bitmap); 450 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 451 assertBitmapQuadColor(bitmap, 452 Color.BLUE, Color.BLACK, Color.BLUE, Color.BLACK, 30); 453 454 // Test that clamping works 455 copyResult = mCopyHelper.request(activity.getVideoView(), new Rect(50, -50, 150, 50), bitmap); 456 assertEquals("Scaled copy request failed", PixelCopy.SUCCESS, copyResult); 457 assertBitmapQuadColor(bitmap, 458 Color.GREEN, Color.GREEN, Color.GREEN, Color.GREEN, 30); 459 } 460 getPixelFloatPos(Bitmap bitmap, float xpos, float ypos)461 private static int getPixelFloatPos(Bitmap bitmap, float xpos, float ypos) { 462 return bitmap.getPixel((int) (bitmap.getWidth() * xpos), (int) (bitmap.getHeight() * ypos)); 463 } 464 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight)465 private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 466 int bottomLeft, int bottomRight) { 467 // Just quickly sample 4 pixels in the various regions. 468 assertEquals("Top left " + Integer.toHexString(topLeft) + ", actual= " 469 + Integer.toHexString(getPixelFloatPos(bitmap, .25f, .25f)), 470 topLeft, getPixelFloatPos(bitmap, .25f, .25f)); 471 assertEquals("Top right", topRight, getPixelFloatPos(bitmap, .75f, .25f)); 472 assertEquals("Bottom left", bottomLeft, getPixelFloatPos(bitmap, .25f, .75f)); 473 assertEquals("Bottom right", bottomRight, getPixelFloatPos(bitmap, .75f, .75f)); 474 } 475 assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight, int threshold)476 private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, 477 int bottomLeft, int bottomRight, int threshold) { 478 // Just quickly sample 4 pixels in the various regions. 479 assertTrue("Top left", pixelsAreSame(topLeft, getPixelFloatPos(bitmap, .25f, .25f), 480 threshold)); 481 assertTrue("Top right", pixelsAreSame(topRight, getPixelFloatPos(bitmap, .75f, .25f), 482 threshold)); 483 assertTrue("Bottom left", pixelsAreSame(bottomLeft, getPixelFloatPos(bitmap, .25f, .75f), 484 threshold)); 485 assertTrue("Bottom right", pixelsAreSame(bottomRight, getPixelFloatPos(bitmap, .75f, .75f), 486 threshold)); 487 } 488 assertBitmapEdgeColor(Bitmap bitmap, int edgeColor)489 private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) { 490 // Just quickly sample a few pixels on the edge and assert 491 // they are edge color, then assert that just inside the edge is a different color 492 assertBitmapColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 0); 493 assertBitmapNotColor("Top edge", bitmap, edgeColor, bitmap.getWidth() / 2, 1); 494 495 assertBitmapColor("Left edge", bitmap, edgeColor, 0, bitmap.getHeight() / 2); 496 assertBitmapNotColor("Left edge", bitmap, edgeColor, 1, bitmap.getHeight() / 2); 497 498 assertBitmapColor("Bottom edge", bitmap, edgeColor, 499 bitmap.getWidth() / 2, bitmap.getHeight() - 1); 500 assertBitmapNotColor("Bottom edge", bitmap, edgeColor, 501 bitmap.getWidth() / 2, bitmap.getHeight() - 2); 502 503 assertBitmapColor("Right edge", bitmap, edgeColor, 504 bitmap.getWidth() - 1, bitmap.getHeight() / 2); 505 assertBitmapNotColor("Right edge", bitmap, edgeColor, 506 bitmap.getWidth() - 2, bitmap.getHeight() / 2); 507 } 508 pixelsAreSame(int ideal, int given, int threshold)509 private boolean pixelsAreSame(int ideal, int given, int threshold) { 510 int error = Math.abs(Color.red(ideal) - Color.red(given)); 511 error += Math.abs(Color.green(ideal) - Color.green(given)); 512 error += Math.abs(Color.blue(ideal) - Color.blue(given)); 513 return (error < threshold); 514 } 515 assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y)516 private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y) { 517 int pixel = bitmap.getPixel(x, y); 518 if (!pixelsAreSame(color, pixel, 10)) { 519 fail(debug + "; expected=" + Integer.toHexString(color) + ", actual=" 520 + Integer.toHexString(pixel)); 521 } 522 } 523 assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y)524 private void assertBitmapNotColor(String debug, Bitmap bitmap, int color, int x, int y) { 525 int pixel = bitmap.getPixel(x, y); 526 if (pixelsAreSame(color, pixel, 10)) { 527 fail(debug + "; actual=" + Integer.toHexString(pixel) 528 + " shouldn't have matched " + Integer.toHexString(color)); 529 } 530 } 531 532 private static class SurfaceTextureRule implements TestRule { 533 private SurfaceTexture mSurfaceTexture = null; 534 private Surface mSurface = null; 535 createIfNecessary()536 private void createIfNecessary() { 537 mSurfaceTexture = new SurfaceTexture(false); 538 mSurface = new Surface(mSurfaceTexture); 539 } 540 getSurface()541 public Surface getSurface() { 542 createIfNecessary(); 543 return mSurface; 544 } 545 546 @Override apply(Statement base, Description description)547 public Statement apply(Statement base, Description description) { 548 return new CreateSurfaceTextureStatement(base); 549 } 550 551 private class CreateSurfaceTextureStatement extends Statement { 552 553 private final Statement mBase; 554 CreateSurfaceTextureStatement(Statement base)555 public CreateSurfaceTextureStatement(Statement base) { 556 mBase = base; 557 } 558 559 @Override evaluate()560 public void evaluate() throws Throwable { 561 try { 562 mBase.evaluate(); 563 } finally { 564 try { 565 if (mSurface != null) mSurface.release(); 566 } catch (Throwable t) {} 567 try { 568 if (mSurfaceTexture != null) mSurfaceTexture.release(); 569 } catch (Throwable t) {} 570 } 571 } 572 } 573 } 574 } 575