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