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 android.opengl.GLES20.GL_COLOR_BUFFER_BIT; 20 import static android.opengl.GLES20.GL_SCISSOR_TEST; 21 import static android.opengl.GLES20.glClear; 22 import static android.opengl.GLES20.glClearColor; 23 import static android.opengl.GLES20.glEnable; 24 import static android.opengl.GLES20.glScissor; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 30 import android.graphics.Bitmap; 31 import android.graphics.Color; 32 import android.graphics.ColorSpace; 33 import android.graphics.Matrix; 34 import android.graphics.Point; 35 import android.graphics.Rect; 36 import android.graphics.RectF; 37 import android.util.Half; 38 import android.view.PixelCopy; 39 import android.view.TextureView; 40 import android.view.View; 41 import android.view.Window; 42 43 import androidx.test.filters.MediumTest; 44 import androidx.test.rule.ActivityTestRule; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.compatibility.common.util.SynchronousPixelCopy; 48 import com.android.compatibility.common.util.WidgetTestUtils; 49 50 import org.junit.Rule; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 54 import java.nio.ByteBuffer; 55 import java.nio.ByteOrder; 56 import java.util.concurrent.TimeoutException; 57 58 @MediumTest 59 @RunWith(AndroidJUnit4.class) 60 public class TextureViewTest { 61 62 static final int EGL_GL_COLORSPACE_SRGB_KHR = 0x3089; 63 static final int EGL_GL_COLORSPACE_LINEAR_KHR = 0x308A; 64 static final int EGL_GL_COLORSPACE_DISPLAY_P3_EXT = 0x3363; 65 static final int EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT = 0x3362; 66 static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490; 67 static final int EGL_GL_COLORSPACE_SCRGB_EXT = 0x3351; 68 static final int EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT = 0x3350; 69 70 @Rule 71 public ActivityTestRule<TextureViewCtsActivity> mActivityRule = 72 new ActivityTestRule<>(TextureViewCtsActivity.class, false, false); 73 74 @Test testFirstFrames()75 public void testFirstFrames() throws Throwable { 76 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 77 activity.waitForEnterAnimationComplete(); 78 79 final Point center = new Point(); 80 final Window[] windowRet = new Window[1]; 81 mActivityRule.runOnUiThread(() -> { 82 View content = activity.findViewById(android.R.id.content); 83 int[] outLocation = new int[2]; 84 content.getLocationOnScreen(outLocation); 85 center.x = outLocation[0] + (content.getWidth() / 2); 86 center.y = outLocation[1] + (content.getHeight() / 2); 87 windowRet[0] = activity.getWindow(); 88 }); 89 final Window window = windowRet[0]; 90 assertTrue(center.x > 0); 91 assertTrue(center.y > 0); 92 waitForColor(window, center, Color.WHITE); 93 activity.waitForSurface(); 94 activity.initGl(); 95 int updatedCount; 96 updatedCount = activity.waitForSurfaceUpdateCount(0); 97 assertEquals(0, updatedCount); 98 activity.drawColor(Color.GREEN); 99 updatedCount = activity.waitForSurfaceUpdateCount(1); 100 assertEquals(1, updatedCount); 101 assertEquals(Color.WHITE, getPixel(window, center)); 102 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, 103 activity.findViewById(android.R.id.content), () -> activity.removeCover()); 104 105 int color = waitForChange(window, center, Color.WHITE); 106 assertEquals(Color.GREEN, color); 107 activity.drawColor(Color.BLUE); 108 updatedCount = activity.waitForSurfaceUpdateCount(2); 109 assertEquals(2, updatedCount); 110 color = waitForChange(window, center, color); 111 assertEquals(Color.BLUE, color); 112 } 113 114 @Test testScaling()115 public void testScaling() throws Throwable { 116 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 117 activity.drawFrame(TextureViewTest::drawGlQuad); 118 final Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); 119 mActivityRule.runOnUiThread(() -> { 120 activity.getTextureView().getBitmap(bitmap); 121 }); 122 PixelCopyTest.assertBitmapQuadColor(bitmap, 123 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 124 } 125 126 @Test testRotateScale()127 public void testRotateScale() throws Throwable { 128 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 129 final TextureView textureView = activity.getTextureView(); 130 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, activity.getTextureView(), null); 131 Matrix rotate = new Matrix(); 132 rotate.setRotate(180, textureView.getWidth() / 2, textureView.getHeight() / 2); 133 activity.drawFrame(rotate, TextureViewTest::drawGlQuad); 134 final Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); 135 mActivityRule.runOnUiThread(() -> { 136 activity.getTextureView().getBitmap(bitmap); 137 }); 138 // Verify the matrix did not rotate content of getTextureView.getBitmap(). 139 PixelCopyTest.assertBitmapQuadColor(bitmap, 140 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 141 142 // Remove cover and calculate TextureView position on the screen. 143 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, 144 activity.findViewById(android.R.id.content), () -> activity.removeCover()); 145 final Rect viewPos = new Rect(); 146 mActivityRule.runOnUiThread(() -> { 147 int[] outLocation = new int[2]; 148 textureView.getLocationOnScreen(outLocation); 149 viewPos.left = outLocation[0]; 150 viewPos.top = outLocation[1]; 151 viewPos.right = viewPos.left + textureView.getWidth(); 152 viewPos.bottom = viewPos.top + textureView.getHeight(); 153 }); 154 155 // Capture the portion of the screen that contains the texture view only. 156 Window window = activity.getWindow(); 157 Bitmap screenshot = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); 158 int result = new SynchronousPixelCopy().request(window, viewPos, screenshot); 159 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 160 // Verify the matrix rotated the TextureView content drawn on the screen. 161 PixelCopyTest.assertBitmapQuadColor(screenshot, 162 Color.BLACK, Color.BLUE, Color.GREEN, Color.RED); 163 } 164 165 @Test testTransformScale()166 public void testTransformScale() throws Throwable { 167 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 168 final TextureView textureView = activity.getTextureView(); 169 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, activity.getTextureView(), null); 170 Matrix transform = new Matrix(); 171 final float translateY = 100.0f; 172 final float scaleY = 0.25f; 173 float[] values = {1, 0, 0, 0, scaleY, translateY, 0, 0, 1}; 174 transform.setValues(values); 175 activity.drawFrame(transform, TextureViewTest::drawGlQuad); 176 final Bitmap bitmap = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888); 177 mActivityRule.runOnUiThread(() -> { 178 activity.getTextureView().getBitmap(bitmap); 179 }); 180 // Verify the matrix did not affect the content of getTextureView.getBitmap(). 181 PixelCopyTest.assertBitmapQuadColor(bitmap, 182 Color.RED, Color.GREEN, Color.BLUE, Color.BLACK); 183 184 // Remove cover and calculate TextureView position on the screen. 185 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, 186 activity.findViewById(android.R.id.content), () -> activity.removeCover()); 187 final Rect viewPos = new Rect(); 188 mActivityRule.runOnUiThread(() -> { 189 int[] outLocation = new int[2]; 190 textureView.getLocationOnScreen(outLocation); 191 viewPos.left = outLocation[0]; 192 viewPos.top = outLocation[1]; 193 viewPos.right = viewPos.left + textureView.getWidth(); 194 viewPos.bottom = viewPos.top + textureView.getHeight(); 195 }); 196 197 // Capture the portion of the screen that contains the texture view only. 198 Window window = activity.getWindow(); 199 Bitmap screenshot = Bitmap.createBitmap(viewPos.width(), viewPos.height(), 200 Bitmap.Config.ARGB_8888); 201 int result = new SynchronousPixelCopy().request(window, viewPos, screenshot); 202 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 203 // Verify the matrix scaled and translated the TextureView content drawn on the screen. 204 // "texturePos" has SurfaceTexture position inside the TextureView. 205 final Rect texturePos = new Rect(0, (int) translateY, viewPos.width(), 206 (int) (viewPos.height() * scaleY + translateY)); 207 208 // Areas not covered by the texture are black, because FrameLayout background is set to 209 // Color.BLACK in TextureViewCtsActivity.onCreate. 210 assertEquals("above texture", Color.BLACK, 211 screenshot.getPixel(10, texturePos.top - 10)); 212 assertEquals("below texture", Color.BLACK, 213 screenshot.getPixel(10, texturePos.bottom + 10)); 214 assertEquals("top left", Color.RED, 215 screenshot.getPixel(texturePos.left + 10, texturePos.top + 10)); 216 assertEquals("top right", Color.GREEN, 217 screenshot.getPixel(texturePos.right - 10, texturePos.top + 10)); 218 assertEquals("Bottom left", Color.BLUE, 219 screenshot.getPixel(texturePos.left + 10, texturePos.bottom - 10)); 220 assertEquals("Bottom right", Color.BLACK, 221 screenshot.getPixel(texturePos.right - 10, texturePos.bottom - 10)); 222 } 223 224 @Test testGetBitmap_8888_P3()225 public void testGetBitmap_8888_P3() throws Throwable { 226 testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_EXT, ColorSpace.get(ColorSpace.Named.DISPLAY_P3), 227 false, false, new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 228 } 229 230 @Test testGetBitmap_8888_PassthroughP3()231 public void testGetBitmap_8888_PassthroughP3() throws Throwable { 232 testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, 233 ColorSpace.get(ColorSpace.Named.DISPLAY_P3), false, true, 234 new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 235 } 236 237 @Test testGetBitmap_FP16_PassthroughP3()238 public void testGetBitmap_FP16_PassthroughP3() throws Throwable { 239 testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, 240 ColorSpace.get(ColorSpace.Named.DISPLAY_P3), true, true, 241 new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 242 } 243 244 @Test testGetBitmap_FP16_LinearP3()245 public void testGetBitmap_FP16_LinearP3() throws Throwable { 246 ColorSpace.Rgb displayP3 = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.DISPLAY_P3); 247 ColorSpace.Rgb linearDisplayP3 = new ColorSpace.Rgb( 248 "Display P3 Linear", 249 displayP3.getTransform(), 250 displayP3.getWhitePoint(), 251 x -> x, 252 x -> x, 253 0.0f, 1.0f 254 ); 255 256 testGetBitmap(EGL_GL_COLORSPACE_DISPLAY_P3_LINEAR_EXT, linearDisplayP3, true, 257 true, new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 258 } 259 260 @Test testGetBitmap_FP16_ExtendedSRGB()261 public void testGetBitmap_FP16_ExtendedSRGB() throws Throwable { 262 // isLinear is "true", because the spec says 263 // GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is GL_LINEAR for EGL_GL_COLORSPACE_SCRGB_EXT. 264 // See https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_gl_colorspace_scrgb.txt. 265 testGetBitmap(EGL_GL_COLORSPACE_SCRGB_EXT, 266 ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB), true, 267 true, new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 268 } 269 270 @Test testGetBitmap_FP16_LinearExtendedSRGB()271 public void testGetBitmap_FP16_LinearExtendedSRGB() throws Throwable { 272 testGetBitmap(EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT, 273 ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), true, 274 true, new FP16Compare(ColorSpace.Named.EXTENDED_SRGB)); 275 } 276 277 @Test testGet565Bitmap_SRGB()278 public void testGet565Bitmap_SRGB() throws Throwable { 279 testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.get(ColorSpace.Named.SRGB), 280 false, false, new SRGBCompare(Bitmap.Config.RGB_565)); 281 } 282 283 @Test testGetBitmap_SRGB()284 public void testGetBitmap_SRGB() throws Throwable { 285 testGetBitmap(EGL_GL_COLORSPACE_SRGB_KHR, ColorSpace.get(ColorSpace.Named.SRGB), 286 false, false, new SRGBCompare(Bitmap.Config.ARGB_8888)); 287 } 288 289 @Test testGetBitmap_SRGBLinear()290 public void testGetBitmap_SRGBLinear() throws Throwable { 291 testGetBitmap(EGL_GL_COLORSPACE_LINEAR_KHR, ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), 292 false, true, new SRGBCompare(Bitmap.Config.ARGB_8888)); 293 } 294 295 /** 296 * Test that verifies TextureView is drawn with bilerp sampling, when the matrix is not 297 * an integer translate or identity. 298 */ 299 @Test testSamplingWithTransform()300 public void testSamplingWithTransform() throws Throwable { 301 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 302 final TextureView textureView = activity.getTextureView(); 303 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, activity.getTextureView(), null); 304 // Remove cover and calculate TextureView position on the screen. 305 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, 306 activity.findViewById(android.R.id.content), () -> activity.removeCover()); 307 308 float[][] matrices = { 309 {1, 0, 0, 0, 1, 0, 0, 0, 1}, // identity matrix 310 {1, 0, 0, 0, 1, 10.3f, 0, 0, 1}, // translation matrix with a fractional offset 311 {1, 0, 0, 0, 0.75f, 0, 0, 0, 1}, // scaling matrix 312 {1, 0, 0, 0, 1, 10f, 0, 0, 1} // translation matrix with an integer offset 313 }; 314 boolean[] nearestSampling = { 315 true, // nearest sampling for identity 316 false, // bilerp sampling for fractional translate 317 false, // bilerp sampling for scaling 318 true // nearest sampling for integer translate 319 }; 320 for (int i = 0; i < nearestSampling.length; i++) { 321 322 Matrix transform = new Matrix(); 323 transform.setValues(matrices[i]); 324 325 // Test draws a set of black & white alternating lines. 326 activity.drawFrame(transform, TextureViewTest::drawGlBlackWhiteLines); 327 328 final Rect viewPos = new Rect(); 329 mActivityRule.runOnUiThread(() -> { 330 int[] outLocation = new int[2]; 331 textureView.getLocationOnScreen(outLocation); 332 viewPos.left = outLocation[0]; 333 viewPos.top = outLocation[1]; 334 viewPos.right = viewPos.left + textureView.getWidth(); 335 viewPos.bottom = viewPos.top + textureView.getHeight(); 336 }); 337 338 // Capture the portion of the screen that contains the texture view only. 339 Window window = activity.getWindow(); 340 Bitmap screenshot = Bitmap.createBitmap(viewPos.width(), viewPos.height(), 341 Bitmap.Config.ARGB_8888); 342 int result = new SynchronousPixelCopy().request(window, viewPos, screenshot); 343 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 344 345 // "texturePos" has SurfaceTexture position inside the TextureView. 346 RectF texturePosF = new RectF(0, 0, viewPos.width(), viewPos.height()); 347 transform.mapRect(texturePosF); 348 //clip parts outside TextureView 349 texturePosF.intersect(0, 0, viewPos.width(), viewPos.height()); 350 Rect texturePos = new Rect((int) Math.ceil(texturePosF.left), 351 (int) Math.ceil(texturePosF.top), (int) Math.floor(texturePosF.right), 352 (int) Math.floor(texturePosF.bottom)); 353 354 int[] pixels = new int[texturePos.width() * texturePos.height()]; 355 screenshot.getPixels(pixels, 0, texturePos.width(), texturePos.left, texturePos.top, 356 texturePos.width(), texturePos.height()); 357 358 boolean success = true; 359 int failPosition = 0; 360 if (nearestSampling[i]) { 361 // Check all pixels are either black or white. 362 for (int j = 0; j < pixels.length; j++) { 363 if (pixels[j] != Color.BLACK && pixels[j] != Color.WHITE) { 364 success = false; 365 failPosition = j; 366 break; 367 } 368 } 369 } else { 370 // Check there are no black nor white pixels, because bilerp sampling changed 371 // pure black/white to a variety of gray intermediates. 372 for (int j = 0; j < pixels.length; j++) { 373 if (pixels[j] == Color.BLACK || pixels[j] == Color.WHITE) { 374 success = false; 375 failPosition = j; 376 break; 377 } 378 } 379 } 380 assertTrue("Unexpected color at position " + failPosition + " = " 381 + Integer.toHexString(pixels[failPosition]) + " " + transform.toString(), 382 success); 383 } 384 } 385 386 interface CompareFunction { getConfig()387 Bitmap.Config getConfig(); getColorSpace()388 ColorSpace getColorSpace(); verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap)389 void verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap); 390 } 391 392 private class FP16Compare implements CompareFunction { 393 private ColorSpace mDstColorSpace; 394 FP16Compare(ColorSpace.Named namedCS)395 FP16Compare(ColorSpace.Named namedCS) { 396 mDstColorSpace = ColorSpace.get(namedCS); 397 } 398 getConfig()399 public Bitmap.Config getConfig() { 400 return Bitmap.Config.RGBA_F16; 401 } 402 getColorSpace()403 public ColorSpace getColorSpace() { 404 return mDstColorSpace; 405 } 406 verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap)407 public void verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap) { 408 // read pixels into buffer and compare using colorspace connector 409 ByteBuffer buffer = ByteBuffer.allocate(dstBitmap.getAllocationByteCount()); 410 buffer.order(ByteOrder.LITTLE_ENDIAN); 411 dstBitmap.copyPixelsToBuffer(buffer); 412 Half alpha = Half.valueOf(buffer.getShort(6)); 413 assertEquals(1.0f, alpha.floatValue(), 0.0f); 414 415 final ColorSpace dstSpace = getColorSpace(); 416 float[] expectedColor = ColorSpace.connect(srcColorSpace, dstSpace).transform(srcColor); 417 float[] outputColor = { 418 Half.valueOf(buffer.getShort(0)).floatValue(), 419 Half.valueOf(buffer.getShort(2)).floatValue(), 420 Half.valueOf(buffer.getShort(4)).floatValue() }; 421 422 assertEquals(expectedColor[0], outputColor[0], 0.01f); 423 assertEquals(expectedColor[1], outputColor[1], 0.01f); 424 assertEquals(expectedColor[2], outputColor[2], 0.01f); 425 } 426 } 427 428 private class SRGBCompare implements CompareFunction { 429 private Bitmap.Config mConfig; 430 SRGBCompare(Bitmap.Config config)431 SRGBCompare(Bitmap.Config config) { 432 mConfig = config; 433 } 434 getConfig()435 public Bitmap.Config getConfig() { 436 return mConfig; 437 } 438 getColorSpace()439 public ColorSpace getColorSpace() { 440 return ColorSpace.get(ColorSpace.Named.SRGB); 441 } 442 verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap)443 public void verify(float[] srcColor, ColorSpace srcColorSpace, Bitmap dstBitmap) { 444 int color = dstBitmap.getPixel(0, 0); 445 assertEquals(1.0f, Color.alpha(color) / 255.0f, 0.0f); 446 assertEquals(srcColor[0], Color.red(color) / 255.0f, 0.01f); 447 assertEquals(srcColor[1], Color.green(color) / 255.0f, 0.01f); 448 assertEquals(srcColor[2], Color.blue(color) / 255.0f, 0.01f); 449 } 450 } 451 452 // isFramebufferLinear is true, when GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is GL_LINEAR. 453 // It is false, when GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is GL_SRGB. testGetBitmap(int eglColorSpace, ColorSpace colorSpace, boolean useHalfFloat, boolean isFramebufferLinear, CompareFunction compareFunction)454 private void testGetBitmap(int eglColorSpace, ColorSpace colorSpace, 455 boolean useHalfFloat, boolean isFramebufferLinear, 456 CompareFunction compareFunction) throws Throwable { 457 final TextureViewCtsActivity activity = mActivityRule.launchActivity(null); 458 activity.waitForSurface(); 459 460 try { 461 activity.initGl(eglColorSpace, useHalfFloat); 462 } catch (RuntimeException e) { 463 // failure to init GL with the right colorspace is not a TextureView failure as some 464 // devices may not support 16-bits or the colorspace extension 465 if (!activity.initGLExtensionUnsupported()) { 466 fail("Unable to initGL : " + e); 467 } 468 return; 469 } 470 471 final float[] inputColor = { 1.0f, 128 / 255.0f, 0.0f}; 472 473 int updatedCount; 474 updatedCount = activity.waitForSurfaceUpdateCount(0); 475 assertEquals(0, updatedCount); 476 activity.drawColor(inputColor[0], inputColor[1], inputColor[2], 1.0f); 477 updatedCount = activity.waitForSurfaceUpdateCount(1); 478 assertEquals(1, updatedCount); 479 480 final Bitmap bitmap = activity.getContents(compareFunction.getConfig(), 481 compareFunction.getColorSpace()); 482 483 // If GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING is GL_SRGB, then glClear will treat the input 484 // color as linear and write a converted sRGB color into the framebuffer. 485 if (isFramebufferLinear) { 486 compareFunction.verify(inputColor, colorSpace, bitmap); 487 } else { 488 ColorSpace.Connector connector; 489 connector = ColorSpace.connect(ColorSpace.get(ColorSpace.Named.LINEAR_SRGB), 490 ColorSpace.get(ColorSpace.Named.SRGB)); 491 float[] outputColor = connector.transform(inputColor); 492 compareFunction.verify(outputColor, colorSpace, bitmap); 493 } 494 } 495 drawGlQuad(int width, int height)496 private static void drawGlQuad(int width, int height) { 497 int cx = width / 2; 498 int cy = height / 2; 499 500 glEnable(GL_SCISSOR_TEST); 501 502 glScissor(0, cy, cx, height - cy); 503 clearColor(Color.RED); 504 505 glScissor(cx, cy, width - cx, height - cy); 506 clearColor(Color.GREEN); 507 508 glScissor(0, 0, cx, cy); 509 clearColor(Color.BLUE); 510 511 glScissor(cx, 0, width - cx, cy); 512 clearColor(Color.BLACK); 513 } 514 drawGlBlackWhiteLines(int width, int height)515 private static void drawGlBlackWhiteLines(int width, int height) { 516 final int lineHeight = 1; 517 glEnable(GL_SCISSOR_TEST); 518 for (int y = 0; y < height / lineHeight; y++) { 519 glScissor(0, lineHeight * y, width, lineHeight); 520 clearColor((y % 2 == 0) ? Color.BLACK : Color.WHITE); 521 } 522 } 523 clearColor(int color)524 private static void clearColor(int color) { 525 glClearColor(Color.red(color) / 255.0f, 526 Color.green(color) / 255.0f, 527 Color.blue(color) / 255.0f, 528 Color.alpha(color) / 255.0f); 529 glClear(GL_COLOR_BUFFER_BIT); 530 } 531 getPixel(Window window, Point point)532 private int getPixel(Window window, Point point) { 533 Bitmap screenshot = Bitmap.createBitmap(window.getDecorView().getWidth(), 534 window.getDecorView().getHeight(), Bitmap.Config.ARGB_8888); 535 int result = new SynchronousPixelCopy().request(window, screenshot); 536 assertEquals("Copy request failed", PixelCopy.SUCCESS, result); 537 int pixel = screenshot.getPixel(point.x, point.y); 538 screenshot.recycle(); 539 return pixel; 540 } 541 waitForColor(Window window, Point point, int color)542 private void waitForColor(Window window, Point point, int color) 543 throws InterruptedException, TimeoutException { 544 for (int i = 0; i < 20; i++) { 545 int pixel = getPixel(window, point); 546 if (pixel == color) { 547 return; 548 } 549 Thread.sleep(16); 550 } 551 throw new TimeoutException(); 552 } 553 waitForChange(Window window, Point point, int color)554 private int waitForChange(Window window, Point point, int color) 555 throws InterruptedException, TimeoutException { 556 for (int i = 0; i < 30; i++) { 557 int pixel = getPixel(window, point); 558 if (pixel != color) { 559 return pixel; 560 } 561 Thread.sleep(16); 562 } 563 throw new TimeoutException(); 564 } 565 } 566