| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.chromoting.cardboard; | |
| 6 | |
| 7 import static org.chromium.chromoting.cardboard.CardboardUtil.makeFloatBuffer; | |
| 8 import static org.chromium.chromoting.cardboard.CardboardUtil.makeRectangularTex
tureBuffer; | |
| 9 | |
| 10 import android.graphics.Bitmap; | |
| 11 import android.graphics.Point; | |
| 12 import android.opengl.GLES20; | |
| 13 | |
| 14 import org.chromium.chromoting.jni.Display; | |
| 15 | |
| 16 import java.nio.FloatBuffer; | |
| 17 | |
| 18 /** | |
| 19 * Chromoting Cardboard activity desktop, which is used to display host desktop. | |
| 20 */ | |
| 21 public class Desktop { | |
| 22 private static final String VERTEX_SHADER = | |
| 23 "uniform mat4 u_CombinedMatrix;" | |
| 24 + "attribute vec4 a_Position;" | |
| 25 + "attribute vec2 a_TexCoordinate;" | |
| 26 + "varying vec2 v_TexCoordinate;" | |
| 27 + "attribute float a_transparency;" | |
| 28 + "varying float v_transparency;" | |
| 29 + "void main() {" | |
| 30 + " v_transparency = a_transparency;" | |
| 31 + " v_TexCoordinate = a_TexCoordinate;" | |
| 32 + " gl_Position = u_CombinedMatrix * a_Position;" | |
| 33 + "}"; | |
| 34 | |
| 35 private static final String FRAGMENT_SHADER = | |
| 36 "precision highp float;" | |
| 37 + "uniform sampler2D u_Texture;" | |
| 38 + "varying vec2 v_TexCoordinate;" | |
| 39 + "const float borderWidth = 0.002;" | |
| 40 + "varying float v_transparency;" | |
| 41 + "void main() {" | |
| 42 + " if (v_TexCoordinate.x > (1.0 - borderWidth) || v_TexCoordinate.
x < borderWidth" | |
| 43 + " || v_TexCoordinate.y > (1.0 - borderWidth)" | |
| 44 + " || v_TexCoordinate.y < borderWidth) {" | |
| 45 + " gl_FragColor = vec4(1.0, 1.0, 1.0, v_transparency);" | |
| 46 + " } else {" | |
| 47 + " vec4 texture = texture2D(u_Texture, v_TexCoordinate);" | |
| 48 + " gl_FragColor = vec4(texture.r, texture.g, texture.b, v_transp
arency);" | |
| 49 + " }" | |
| 50 + "}"; | |
| 51 | |
| 52 private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextur
eBuffer( | |
| 53 0.0f, 1.0f, 0.0f, 1.0f); | |
| 54 | |
| 55 private static final int POSITION_DATA_SIZE = 3; | |
| 56 private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; | |
| 57 | |
| 58 // Fix the desktop height and adjust width accordingly. | |
| 59 private static final float HALF_HEIGHT = 1.0f; | |
| 60 | |
| 61 // Number of vertices passed to glDrawArrays(). | |
| 62 private static final int VERTICES_NUMBER = 6; | |
| 63 | |
| 64 private final Display mDisplay; | |
| 65 | |
| 66 private int mVertexShaderHandle; | |
| 67 private int mFragmentShaderHandle; | |
| 68 private int mProgramHandle; | |
| 69 private int mCombinedMatrixHandle; | |
| 70 private int mTextureUniformHandle; | |
| 71 private int mPositionHandle; | |
| 72 private int mTransparentHandle; | |
| 73 private int mTextureDataHandle; | |
| 74 private int mTextureCoordinateHandle; | |
| 75 private FloatBuffer mPosition; | |
| 76 private float mHalfWidth; | |
| 77 | |
| 78 // Lock to allow multithreaded access to mHalfWidth. | |
| 79 private final Object mHalfWidthLock = new Object(); | |
| 80 | |
| 81 private Bitmap mVideoFrame; | |
| 82 | |
| 83 // Lock to allow multithreaded access to mVideoFrame. | |
| 84 private final Object mVideoFrameLock = new Object(); | |
| 85 | |
| 86 // Flag to indicate whether to reload the desktop texture. | |
| 87 private boolean mReloadTexture; | |
| 88 | |
| 89 // Lock to allow multithreaded access to mReloadTexture. | |
| 90 private final Object mReloadTextureLock = new Object(); | |
| 91 | |
| 92 public Desktop(Display display) { | |
| 93 mDisplay = display; | |
| 94 mVertexShaderHandle = | |
| 95 ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADE
R); | |
| 96 mFragmentShaderHandle = | |
| 97 ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_S
HADER); | |
| 98 mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, | |
| 99 mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordin
ate", | |
| 100 "u_CombinedMatrix", "u_Texture"}); | |
| 101 mCombinedMatrixHandle = | |
| 102 GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); | |
| 103 mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_T
exture"); | |
| 104 mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position
"); | |
| 105 mTransparentHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_trans
parency"); | |
| 106 mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a
_TexCoordinate"); | |
| 107 mTextureDataHandle = TextureHelper.createTextureHandle(); | |
| 108 } | |
| 109 | |
| 110 /** | |
| 111 * Draw the desktop. Make sure {@link hasVideoFrame} returns true. | |
| 112 */ | |
| 113 public void draw(float[] combinedMatrix, boolean isTransparent) { | |
| 114 GLES20.glUseProgram(mProgramHandle); | |
| 115 | |
| 116 // Pass in model view project matrix. | |
| 117 GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatri
x, 0); | |
| 118 | |
| 119 // Pass in texture coordinate. | |
| 120 GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINAT
E_DATA_SIZE, | |
| 121 GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); | |
| 122 GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); | |
| 123 | |
| 124 GLES20.glVertexAttribPointer(mPositionHandle, POSITION_DATA_SIZE, GLES20
.GL_FLOAT, false, | |
| 125 0, mPosition); | |
| 126 GLES20.glEnableVertexAttribArray(mPositionHandle); | |
| 127 | |
| 128 // Enable transparency. | |
| 129 GLES20.glEnable(GLES20.GL_BLEND); | |
| 130 GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); | |
| 131 | |
| 132 // Pass in transparency. | |
| 133 GLES20.glVertexAttrib1f(mTransparentHandle, isTransparent ? 0.5f : 1.0f)
; | |
| 134 | |
| 135 // Link texture data with texture unit. | |
| 136 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); | |
| 137 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); | |
| 138 GLES20.glUniform1i(mTextureUniformHandle, 0); | |
| 139 | |
| 140 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); | |
| 141 | |
| 142 GLES20.glDisable(GLES20.GL_BLEND); | |
| 143 GLES20.glDisableVertexAttribArray(mPositionHandle); | |
| 144 GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle); | |
| 145 } | |
| 146 | |
| 147 /** | |
| 148 * Update the desktop frame data based on the mVideoFrame. Note here we fix
the | |
| 149 * height of the desktop and vary width accordingly. | |
| 150 */ | |
| 151 private void updateVideoFrame(Bitmap videoFrame) { | |
| 152 float newHalfDesktopWidth; | |
| 153 synchronized (mVideoFrameLock) { | |
| 154 mVideoFrame = videoFrame; | |
| 155 TextureHelper.linkTexture(mTextureDataHandle, videoFrame); | |
| 156 newHalfDesktopWidth = videoFrame.getWidth() * HALF_HEIGHT / videoFra
me.getHeight(); | |
| 157 } | |
| 158 | |
| 159 synchronized (mHalfWidthLock) { | |
| 160 if (Math.abs(mHalfWidth - newHalfDesktopWidth) > 0.0001) { | |
| 161 mHalfWidth = newHalfDesktopWidth; | |
| 162 mPosition = makeFloatBuffer(new float[] { | |
| 163 // Desktop model coordinates. | |
| 164 -mHalfWidth, HALF_HEIGHT, 0.0f, | |
| 165 -mHalfWidth, -HALF_HEIGHT, 0.0f, | |
| 166 mHalfWidth, HALF_HEIGHT, 0.0f, | |
| 167 -mHalfWidth, -HALF_HEIGHT, 0.0f, | |
| 168 mHalfWidth, -HALF_HEIGHT, 0.0f, | |
| 169 mHalfWidth, HALF_HEIGHT, 0.0f | |
| 170 }); | |
| 171 } | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 /** | |
| 176 * Clean up opengl data. | |
| 177 */ | |
| 178 public void cleanup() { | |
| 179 GLES20.glDeleteShader(mVertexShaderHandle); | |
| 180 GLES20.glDeleteShader(mFragmentShaderHandle); | |
| 181 GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); | |
| 182 } | |
| 183 | |
| 184 /** | |
| 185 * Return true if video frame data are already loaded in. | |
| 186 */ | |
| 187 public boolean hasVideoFrame() { | |
| 188 synchronized (mVideoFrameLock) { | |
| 189 return mVideoFrame != null; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 public float getHalfHeight() { | |
| 194 return HALF_HEIGHT; | |
| 195 } | |
| 196 | |
| 197 public float getHalfWidth() { | |
| 198 synchronized (mHalfWidthLock) { | |
| 199 return mHalfWidth; | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 /** | |
| 204 * Get desktop height and width in pixels. | |
| 205 */ | |
| 206 public Point getFrameSizePixels() { | |
| 207 synchronized (mVideoFrameLock) { | |
| 208 return new Point(mVideoFrame == null ? 0 : mVideoFrame.getWidth(), | |
| 209 mVideoFrame == null ? 0 : mVideoFrame.getHeight()); | |
| 210 } | |
| 211 } | |
| 212 | |
| 213 /** | |
| 214 * Link desktop texture if {@link reloadTexture} was previously called. | |
| 215 * Invoked from {@link com.google.vrtoolkit.cardboard.CardboardView.StereoRe
nderer.onNewFrame} | |
| 216 * so that both eyes will have the same texture. | |
| 217 */ | |
| 218 public void maybeLoadDesktopTexture() { | |
| 219 synchronized (mReloadTextureLock) { | |
| 220 if (!mReloadTexture) { | |
| 221 return; | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 // TODO(shichengfeng): Record the time desktop drawing takes. | |
| 226 Bitmap bitmap = mDisplay.getVideoFrame(); | |
| 227 | |
| 228 if (bitmap == null) { | |
| 229 // This can happen if the client is connected, but a complete video
frame has not yet | |
| 230 // been decoded. | |
| 231 return; | |
| 232 } | |
| 233 | |
| 234 updateVideoFrame(bitmap); | |
| 235 | |
| 236 synchronized (mReloadTextureLock) { | |
| 237 mReloadTexture = false; | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 /** | |
| 242 * Inform this object that a new video frame should be rendered. | |
| 243 * Called from native display thread. | |
| 244 */ | |
| 245 public void reloadTexture() { | |
| 246 synchronized (mReloadTextureLock) { | |
| 247 mReloadTexture = true; | |
| 248 } | |
| 249 } | |
| 250 } | |
| OLD | NEW |