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