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