• 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 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