1b28678ce70d9e9f57ef50364f2054c4a1d630149magjed/*
2b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * libjingle
3b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * Copyright 2015 Google Inc.
4b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *
5b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * Redistribution and use in source and binary forms, with or without
6b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * modification, are permitted provided that the following conditions are met:
7b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *
8b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *  1. Redistributions of source code must retain the above copyright notice,
9b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *     this list of conditions and the following disclaimer.
10b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *  2. Redistributions in binary form must reproduce the above copyright notice,
11b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *     this list of conditions and the following disclaimer in the documentation
12b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *     and/or other materials provided with the distribution.
13b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *  3. The name of the author may not be used to endorse or promote products
14b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *     derived from this software without specific prior written permission.
15b28678ce70d9e9f57ef50364f2054c4a1d630149magjed *
16b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25b28678ce70d9e9f57ef50364f2054c4a1d630149magjed * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26b28678ce70d9e9f57ef50364f2054c4a1d630149magjed */
27b28678ce70d9e9f57ef50364f2054c4a1d630149magjedpackage org.webrtc;
28b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
298c425aa8f66fc2f06df402a0f2163cb53373856fmagjedimport android.graphics.SurfaceTexture;
308c425aa8f66fc2f06df402a0f2163cb53373856fmagjedimport android.opengl.GLES20;
31b28678ce70d9e9f57ef50364f2054c4a1d630149magjedimport android.test.ActivityTestCase;
3224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvertimport android.test.suitebuilder.annotation.MediumTest;
338c425aa8f66fc2f06df402a0f2163cb53373856fmagjedimport android.test.suitebuilder.annotation.SmallTest;
34b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
35b28678ce70d9e9f57ef50364f2054c4a1d630149magjedimport java.nio.ByteBuffer;
36b28678ce70d9e9f57ef50364f2054c4a1d630149magjedimport java.util.Random;
37b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
3824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvertpublic final class GlRectDrawerTest extends ActivityTestCase {
39b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  // Resolution of the test image.
40b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  private static final int WIDTH = 16;
41b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  private static final int HEIGHT = 16;
42b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  // Seed for random pixel creation.
43b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  private static final int SEED = 42;
44b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  // When comparing pixels, allow some slack for float arithmetic and integer rounding.
4540455d6f37fda78ea069a51d95f28994bd736864perkj  private static final float MAX_DIFF = 1.5f;
46b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
47b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  private static float normalizedByte(byte b) {
48b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    return (b & 0xFF) / 255.0f;
49b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  }
50b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
51b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  private static float saturatedConvert(float c) {
52b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    return 255.0f * Math.max(0, Math.min(c, 1));
53b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  }
54b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
55495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  // Assert RGB ByteBuffers are pixel perfect identical.
56495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  private static void assertEquals(int width, int height, ByteBuffer actual, ByteBuffer expected) {
57495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    actual.rewind();
58495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    expected.rewind();
59495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    assertEquals(actual.remaining(), width * height * 3);
60495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    assertEquals(expected.remaining(), width * height * 3);
61495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    for (int y = 0; y < height; ++y) {
62495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      for (int x = 0; x < width; ++x) {
63495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int actualR = actual.get() & 0xFF;
64495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int actualG = actual.get() & 0xFF;
65495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int actualB = actual.get() & 0xFF;
66495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int expectedR = expected.get() & 0xFF;
67495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int expectedG = expected.get() & 0xFF;
68495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        final int expectedB = expected.get() & 0xFF;
69495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        if (actualR != expectedR || actualG != expectedG || actualB != expectedB) {
70495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert          fail("ByteBuffers of size " + width + "x" + height + " not equal at position "
71495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert              + "(" +  x + ", " + y + "). Expected color (R,G,B): "
72495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert              + "(" + expectedR + ", " + expectedG + ", " + expectedB + ")"
73495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert              + " but was: " + "(" + actualR + ", " + actualG + ", " + actualB + ").");
74495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        }
75495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      }
76495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    }
77495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  }
78495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
79495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  // Convert RGBA ByteBuffer to RGB ByteBuffer.
80495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  private static ByteBuffer stripAlphaChannel(ByteBuffer rgbaBuffer) {
81495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    rgbaBuffer.rewind();
82495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    assertEquals(rgbaBuffer.remaining() % 4, 0);
83495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final int numberOfPixels = rgbaBuffer.remaining() / 4;
84495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final ByteBuffer rgbBuffer = ByteBuffer.allocateDirect(numberOfPixels * 3);
85495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    while (rgbaBuffer.hasRemaining()) {
86495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      // Copy RGB.
87495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      for (int channel = 0; channel < 3; ++channel) {
88495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        rgbBuffer.put(rgbaBuffer.get());
89495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      }
90495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      // Drop alpha.
91495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      rgbaBuffer.get();
92495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    }
93495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    return rgbBuffer;
94495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  }
95495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
96495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  @SmallTest
9724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  public void testRgbRendering() {
98495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Create EGL base with a pixel buffer as display output.
9903f80ebb8310e5f04ced856f7ec8f14b94a0f47enisse    final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
100495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    eglBase.createPbufferSurface(WIDTH, HEIGHT);
101495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    eglBase.makeCurrent();
102495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
103495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Create RGB byte buffer plane with random content.
104495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final ByteBuffer rgbPlane = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 3);
105495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final Random random = new Random(SEED);
106495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    random.nextBytes(rgbPlane.array());
107495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
108495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Upload the RGB byte buffer data as a texture.
109495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final int rgbTexture = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
110495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
111495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, rgbTexture);
112495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, WIDTH,
113495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert        HEIGHT, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, rgbPlane);
114495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GlUtil.checkNoGLES2Error("glTexImage2D");
115495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
116495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Draw the RGB frame onto the pixel buffer.
117495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final GlRectDrawer drawer = new GlRectDrawer();
11851254331ccb3838b03ed0c630f7e3d5d402d1919Magnus Jedvert    drawer.drawRgb(rgbTexture, RendererCommon.identityMatrix(), 0, 0, WIDTH, HEIGHT);
119495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
120495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Download the pixels in the pixel buffer as RGBA. Not all platforms support RGB, e.g. Nexus 9.
121495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    final ByteBuffer rgbaData = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 4);
122495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GLES20.glReadPixels(0, 0, WIDTH, HEIGHT, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaData);
123495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GlUtil.checkNoGLES2Error("glReadPixels");
124495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
125495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Assert rendered image is pixel perfect to source RGB.
126495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    assertEquals(WIDTH, HEIGHT, stripAlphaChannel(rgbaData), rgbPlane);
127495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
128495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    drawer.release();
129495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GLES20.glDeleteTextures(1, new int[] {rgbTexture}, 0);
130495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    eglBase.release();
131495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert  }
132495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert
133b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  @SmallTest
13424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  public void testYuvRendering() {
135b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Create EGL base with a pixel buffer as display output.
13603f80ebb8310e5f04ced856f7ec8f14b94a0f47enisse    EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
137b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    eglBase.createPbufferSurface(WIDTH, HEIGHT);
138b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    eglBase.makeCurrent();
139b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
140b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Create YUV byte buffer planes with random content.
141b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    final ByteBuffer[] yuvPlanes = new ByteBuffer[3];
142b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    final Random random = new Random(SEED);
143b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    for (int i = 0; i < 3; ++i) {
144b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      yuvPlanes[i] = ByteBuffer.allocateDirect(WIDTH * HEIGHT);
145b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      random.nextBytes(yuvPlanes[i].array());
146b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    }
147b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
148b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Generate 3 texture ids for Y/U/V.
149b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    final int yuvTextures[] = new int[3];
150b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    for (int i = 0; i < 3; i++)  {
1511a591ddc7e6529e57a27f4a8f133ddd14a7ead16Magnus Jedvert      yuvTextures[i] = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
152b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    }
153b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
154b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Upload the YUV byte buffer data as textures.
155b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    for (int i = 0; i < 3; ++i) {
156b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
157b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
158b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, WIDTH,
159b28678ce70d9e9f57ef50364f2054c4a1d630149magjed          HEIGHT, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yuvPlanes[i]);
160495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert      GlUtil.checkNoGLES2Error("glTexImage2D");
161b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    }
162b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
163b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Draw the YUV frame onto the pixel buffer.
164b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    final GlRectDrawer drawer = new GlRectDrawer();
16551254331ccb3838b03ed0c630f7e3d5d402d1919Magnus Jedvert    drawer.drawYuv(yuvTextures, RendererCommon.identityMatrix(), 0, 0, WIDTH, HEIGHT);
166b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
167495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    // Download the pixels in the pixel buffer as RGBA. Not all platforms support RGB, e.g. Nexus 9.
168b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    final ByteBuffer data = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 4);
169b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    GLES20.glReadPixels(0, 0, WIDTH, HEIGHT, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data);
170495d2fdd6569c252aebf0bae5c7cab0a207dd6dcMagnus Jedvert    GlUtil.checkNoGLES2Error("glReadPixels");
171b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
172b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    // Compare the YUV data with the RGBA result.
173b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    for (int y = 0; y < HEIGHT; ++y) {
174b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      for (int x = 0; x < WIDTH; ++x) {
175b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        // YUV color space. Y in [0, 1], UV in [-0.5, 0.5]. The constants are taken from the YUV
176b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        // fragment shader code in GlRectDrawer.
177b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float y_luma = normalizedByte(yuvPlanes[0].get());
178b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float u_chroma = normalizedByte(yuvPlanes[1].get()) - 0.5f;
179b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float v_chroma = normalizedByte(yuvPlanes[2].get()) - 0.5f;
180b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        // Expected color in unrounded RGB [0.0f, 255.0f].
181b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float expectedRed = saturatedConvert(y_luma + 1.403f * v_chroma);
182b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float expectedGreen =
183b28678ce70d9e9f57ef50364f2054c4a1d630149magjed            saturatedConvert(y_luma - 0.344f * u_chroma - 0.714f * v_chroma);
184b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final float expectedBlue = saturatedConvert(y_luma + 1.77f * u_chroma);
185b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
186b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        // Actual color in RGB8888.
187b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final int actualRed = data.get() & 0xFF;
188b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final int actualGreen = data.get() & 0xFF;
189b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final int actualBlue = data.get() & 0xFF;
190b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        final int actualAlpha = data.get() & 0xFF;
191b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
192b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        // Assert rendered image is close to pixel perfect from source YUV.
193b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        assertTrue(Math.abs(actualRed - expectedRed) < MAX_DIFF);
194b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        assertTrue(Math.abs(actualGreen - expectedGreen) < MAX_DIFF);
195b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        assertTrue(Math.abs(actualBlue - expectedBlue) < MAX_DIFF);
196b28678ce70d9e9f57ef50364f2054c4a1d630149magjed        assertEquals(actualAlpha, 255);
197b28678ce70d9e9f57ef50364f2054c4a1d630149magjed      }
198b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    }
199b28678ce70d9e9f57ef50364f2054c4a1d630149magjed
200b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    drawer.release();
201b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    GLES20.glDeleteTextures(3, yuvTextures, 0);
202b28678ce70d9e9f57ef50364f2054c4a1d630149magjed    eglBase.release();
203b28678ce70d9e9f57ef50364f2054c4a1d630149magjed  }
20424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
20524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  /**
20624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   * The purpose here is to test GlRectDrawer.oesDraw(). Unfortunately, there is no easy way to
20724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   * create an OES texture, which is needed for input to oesDraw(). Most of the test is concerned
20824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   * with creating OES textures in the following way:
20924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Create SurfaceTexture with help from SurfaceTextureHelper.
21024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Create an EglBase with the SurfaceTexture as EGLSurface.
21124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Upload RGB texture with known content.
21224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Draw the RGB texture onto the EglBase with the SurfaceTexture as target.
21324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Wait for an OES texture to be produced.
21424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   * The actual oesDraw() test is this:
21524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Create an EglBase with a pixel buffer as target.
21624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Render the OES texture onto the pixel buffer.
21724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   *  - Read back the pixel buffer and compare it with the known RGB data.
21824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert   */
21924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  @MediumTest
22024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  public void testOesRendering() throws InterruptedException {
22124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    /**
22224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert     * Stub class to convert RGB ByteBuffers to OES textures by drawing onto a SurfaceTexture.
22324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert     */
22424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    class StubOesTextureProducer {
22524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      private final EglBase eglBase;
22624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      private final GlRectDrawer drawer;
22724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      private final int rgbTexture;
22824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
22924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      public StubOesTextureProducer(
23040455d6f37fda78ea069a51d95f28994bd736864perkj          EglBase.Context sharedContext, SurfaceTexture surfaceTexture, int width,
23140455d6f37fda78ea069a51d95f28994bd736864perkj          int height) {
23203f80ebb8310e5f04ced856f7ec8f14b94a0f47enisse        eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PLAIN);
23324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        surfaceTexture.setDefaultBufferSize(width, height);
23424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.createSurface(surfaceTexture);
23524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        assertEquals(eglBase.surfaceWidth(), width);
23624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        assertEquals(eglBase.surfaceHeight(), height);
23724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
23824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        drawer = new GlRectDrawer();
23924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
24024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.makeCurrent();
24124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        rgbTexture = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D);
24224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      }
24324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
24424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      public void draw(ByteBuffer rgbPlane) {
24524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.makeCurrent();
24624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
24724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        // Upload RGB data to texture.
24824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
24924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, rgbTexture);
25024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGB, WIDTH,
25124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert            HEIGHT, 0, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, rgbPlane);
25224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        // Draw the RGB data onto the SurfaceTexture.
25351254331ccb3838b03ed0c630f7e3d5d402d1919Magnus Jedvert        drawer.drawRgb(rgbTexture, RendererCommon.identityMatrix(), 0, 0, WIDTH, HEIGHT);
25424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.swapBuffers();
25524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      }
25624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
25724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      public void release() {
25824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.makeCurrent();
25924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        drawer.release();
26024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        GLES20.glDeleteTextures(1, new int[] {rgbTexture}, 0);
26124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        eglBase.release();
26224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert      }
26324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    }
26424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
26524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Create EGL base with a pixel buffer as display output.
26603f80ebb8310e5f04ced856f7ec8f14b94a0f47enisse    final EglBase eglBase = EglBase.create(null, EglBase.CONFIG_PIXEL_BUFFER);
26724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    eglBase.createPbufferSurface(WIDTH, HEIGHT);
26824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
26924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Create resources for generating OES textures.
27024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final SurfaceTextureHelper surfaceTextureHelper =
27140455d6f37fda78ea069a51d95f28994bd736864perkj        SurfaceTextureHelper.create(eglBase.getEglBaseContext());
27224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final StubOesTextureProducer oesProducer = new StubOesTextureProducer(
27340455d6f37fda78ea069a51d95f28994bd736864perkj        eglBase.getEglBaseContext(), surfaceTextureHelper.getSurfaceTexture(), WIDTH, HEIGHT);
27424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final SurfaceTextureHelperTest.MockTextureListener listener =
27524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert        new SurfaceTextureHelperTest.MockTextureListener();
27624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    surfaceTextureHelper.setListener(listener);
27724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
27824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Create RGB byte buffer plane with random content.
27924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final ByteBuffer rgbPlane = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 3);
28024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final Random random = new Random(SEED);
28124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    random.nextBytes(rgbPlane.array());
28224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
28324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Draw the frame and block until an OES texture is delivered.
28424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    oesProducer.draw(rgbPlane);
28524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    listener.waitForNewFrame();
28624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
28724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Real test starts here.
28824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Draw the OES texture on the pixel buffer.
28924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    eglBase.makeCurrent();
29024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final GlRectDrawer drawer = new GlRectDrawer();
29151254331ccb3838b03ed0c630f7e3d5d402d1919Magnus Jedvert    drawer.drawOes(listener.oesTextureId, listener.transformMatrix, 0, 0, WIDTH, HEIGHT);
29224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
29324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Download the pixels in the pixel buffer as RGBA. Not all platforms support RGB, e.g. Nexus 9.
29424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    final ByteBuffer rgbaData = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 4);
29524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    GLES20.glReadPixels(0, 0, WIDTH, HEIGHT, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaData);
29624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    GlUtil.checkNoGLES2Error("glReadPixels");
29724b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
29824b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    // Assert rendered image is pixel perfect to source RGB.
29924b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    assertEquals(WIDTH, HEIGHT, stripAlphaChannel(rgbaData), rgbPlane);
30024b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert
30124b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    drawer.release();
30224b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    surfaceTextureHelper.returnTextureFrame();
30324b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    oesProducer.release();
30424b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    surfaceTextureHelper.disconnect();
30524b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert    eglBase.release();
30624b52f8322ae225f5e95b9f4d33c976add803a81Magnus Jedvert  }
307b28678ce70d9e9f57ef50364f2054c4a1d630149magjed}
308