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.uirendering.cts.testclasses; 18 19 import static org.junit.Assert.assertEquals; 20 21 import android.animation.Animator; 22 import android.animation.ValueAnimator; 23 import android.content.Context; 24 import android.graphics.Bitmap; 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.Paint; 28 import android.graphics.Picture; 29 import android.graphics.Rect; 30 import android.os.Handler; 31 import android.support.test.filters.LargeTest; 32 import android.support.test.runner.AndroidJUnit4; 33 import android.uirendering.cts.R; 34 import android.uirendering.cts.bitmapcomparers.MSSIMComparer; 35 import android.uirendering.cts.bitmapverifiers.BitmapVerifier; 36 import android.uirendering.cts.bitmapverifiers.ColorCountVerifier; 37 import android.uirendering.cts.bitmapverifiers.RectVerifier; 38 import android.uirendering.cts.testinfrastructure.ActivityTestBase; 39 import android.uirendering.cts.testinfrastructure.ViewInitializer; 40 import android.view.FrameMetrics; 41 import android.view.View; 42 import android.view.ViewAnimationUtils; 43 import android.view.Window; 44 import android.widget.FrameLayout; 45 46 import org.junit.Assert; 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.util.Arrays; 51 52 @LargeTest 53 @RunWith(AndroidJUnit4.class) 54 public class BitmapTests extends ActivityTestBase { 55 class BitmapView extends View { 56 private Bitmap mBitmap; 57 private int mColor; 58 59 public BitmapView(Context context) { 60 super(context); 61 mBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 62 setColor(Color.BLUE); 63 } 64 65 @Override 66 protected void onDraw(Canvas canvas) { 67 canvas.drawBitmap(mBitmap, new Rect(0, 0, 1, 1), canvas.getClipBounds(), null); 68 } 69 70 public void setColor(int color) { 71 mColor = color; 72 mBitmap.setPixel(0, 0, color); 73 } 74 75 public int getColor() { 76 return mColor; 77 } 78 } 79 80 /* 81 * The following test verifies that bitmap changes during render thread animation won't 82 * be visible: we changed a bitmap from blue to red during circular reveal (an RT animation), 83 * and changed it back to blue before the end of the animation; we should never see any 84 * red pixel. 85 */ 86 @Test 87 public void testChangeDuringRtAnimation() { 88 class RtOnlyFrameCounter implements Window.OnFrameMetricsAvailableListener { 89 private int count = 0; 90 91 @Override 92 public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, 93 int dropCountSinceLastInvocation) { 94 if (frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION) == 0 95 && frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION) == 0 96 && frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION) == 0) { 97 count++; 98 }; 99 } 100 101 public boolean isLargeEnough() { 102 return count >= 5; 103 } 104 } 105 106 RtOnlyFrameCounter counter = new RtOnlyFrameCounter(); 107 108 ViewInitializer initializer = new ViewInitializer() { 109 Animator mAnimator; 110 111 @Override 112 public void initializeView(View view) { 113 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 114 115 final BitmapView child = new BitmapView(view.getContext()); 116 child.setLayoutParams(new FrameLayout.LayoutParams(50, 50)); 117 root.addView(child); 118 119 mAnimator = ViewAnimationUtils.createCircularReveal(child, 0, 0, 0, 90); 120 mAnimator.setDuration(3000); 121 mAnimator.start(); 122 123 Handler handler = new Handler(); 124 handler.postDelayed(new Runnable() { 125 @Override 126 public void run() { 127 child.setColor(Color.RED); 128 try { 129 Thread.sleep(1000); 130 } catch (Exception e) { 131 // do nothing 132 } 133 child.setColor(Color.BLUE); 134 } 135 }, 1000); 136 getActivity().getWindow().addOnFrameMetricsAvailableListener(counter, handler); 137 } 138 139 @Override 140 public void teardownView() { 141 mAnimator.cancel(); 142 getActivity().getWindow().removeOnFrameMetricsAvailableListener(counter); 143 } 144 }; 145 146 createTest() 147 .addLayout(R.layout.frame_layout, initializer, true) 148 .runWithAnimationVerifier(new ColorCountVerifier(Color.RED, 0)); 149 150 Assert.assertTrue(counter.isLargeEnough()); 151 } 152 153 /* 154 * The following test verifies that bitmap changes during UI thread animation are 155 * visible: we keep changing a bitmap's color between red and blue in sync with the 156 * background, and we should only see pure blue or red. 157 */ 158 @Test 159 public void testChangeDuringUiAnimation() { 160 class BlueOrRedVerifier extends BitmapVerifier { 161 @Override 162 public boolean verify(int[] bitmap, int offset, int stride, int width, int height) { 163 MSSIMComparer comparer = new MSSIMComparer(0.99); 164 int[] red = new int[offset + height * stride]; 165 Arrays.fill(red, Color.RED); 166 int[] blue = new int[offset + height * stride]; 167 Arrays.fill(blue, Color.BLUE); 168 boolean isRed = comparer.verifySame(red, bitmap, offset, stride, width, height); 169 boolean isBlue = comparer.verifySame(blue, bitmap, offset, stride, width, height); 170 return isRed || isBlue; 171 } 172 } 173 174 ViewInitializer initializer = new ViewInitializer() { 175 ValueAnimator mAnimator; 176 177 @Override 178 public void initializeView(View view) { 179 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 180 root.setBackgroundColor(Color.BLUE); 181 182 final BitmapView child = new BitmapView(view.getContext()); 183 184 // The child size is strictly less than the test canvas size, 185 // and we are moving it up and down inside the canvas. 186 child.setLayoutParams(new FrameLayout.LayoutParams(ActivityTestBase.TEST_WIDTH / 2, 187 ActivityTestBase.TEST_HEIGHT / 2)); 188 root.addView(child); 189 child.setColor(Color.BLUE); 190 191 mAnimator = ValueAnimator.ofInt(0, ActivityTestBase.TEST_HEIGHT / 2); 192 mAnimator.setRepeatMode(mAnimator.REVERSE); 193 mAnimator.setRepeatCount(mAnimator.INFINITE); 194 mAnimator.setDuration(400); 195 mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 196 @Override 197 public void onAnimationUpdate(ValueAnimator animation) { 198 int v = (Integer) mAnimator.getAnimatedValue(); 199 child.setTranslationY(v); 200 if (child.getColor() == Color.BLUE) { 201 root.setBackgroundColor(Color.RED); 202 child.setColor(Color.RED); 203 } else { 204 root.setBackgroundColor(Color.BLUE); 205 child.setColor(Color.BLUE); 206 } 207 } 208 }); 209 mAnimator.start(); 210 } 211 212 @Override 213 public void teardownView() { 214 mAnimator.cancel(); 215 } 216 }; 217 218 createTest() 219 .addLayout(R.layout.frame_layout, initializer, true) 220 .runWithAnimationVerifier(new BlueOrRedVerifier()); 221 } 222 223 @Test 224 public void testCreateFromPicture() { 225 final Rect rect = new Rect(10, 10, 80, 80); 226 Picture picture = new Picture(); 227 { 228 Canvas canvas = picture.beginRecording(TEST_WIDTH, TEST_HEIGHT); 229 Paint p = new Paint(); 230 p.setAntiAlias(false); 231 p.setColor(Color.BLUE); 232 canvas.drawRect(rect, p); 233 picture.endRecording(); 234 } 235 Bitmap bitmap = Bitmap.createBitmap(picture, picture.getWidth(), 236 picture.getHeight(), Bitmap.Config.ARGB_8888); 237 assertEquals(TEST_WIDTH, bitmap.getWidth()); 238 assertEquals(TEST_HEIGHT, bitmap.getHeight()); 239 assertEquals(Bitmap.Config.ARGB_8888, bitmap.getConfig()); 240 createTest().addCanvasClient((canvas, width, height) -> { 241 canvas.drawBitmap(bitmap, 0, 0, null); 242 }, true).runWithVerifier(new RectVerifier(Color.WHITE, Color.BLUE, rect)); 243 } 244 } 245