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