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 |