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