| 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.graphics.PointF; | |
| 13 import android.opengl.GLES20; | |
| 14 | |
| 15 import org.chromium.chromoting.InputStub; | |
| 16 import org.chromium.chromoting.jni.Client; | |
| 17 import org.chromium.chromoting.jni.Display; | |
| 18 | |
| 19 import java.nio.FloatBuffer; | |
| 20 | |
| 21 /** | |
| 22 * Cardboard activity desktop cursor that is used to render the image of the mou
se | |
| 23 * cursor onto the desktop. | |
| 24 */ | |
| 25 public class Cursor { | |
| 26 private static final String VERTEX_SHADER = | |
| 27 "uniform mat4 u_CombinedMatrix;" | |
| 28 + "attribute vec4 a_Position;" | |
| 29 + "attribute vec2 a_TexCoordinate;" | |
| 30 + "varying vec2 v_TexCoordinate;" | |
| 31 + "void main() {" | |
| 32 + " v_TexCoordinate = a_TexCoordinate;" | |
| 33 + " gl_Position = u_CombinedMatrix * a_Position;" | |
| 34 + "}"; | |
| 35 | |
| 36 private static final String FRAGMENT_SHADER = | |
| 37 "precision mediump float;" | |
| 38 + "uniform sampler2D u_Texture;" | |
| 39 + "varying vec2 v_TexCoordinate;" | |
| 40 + "void main() {" | |
| 41 + " gl_FragColor = texture2D(u_Texture, v_TexCoordinate);" | |
| 42 + "}"; | |
| 43 | |
| 44 private static final FloatBuffer TEXTURE_COORDINATES = makeRectangularTextur
eBuffer( | |
| 45 0.0f, 1.0f, 0.0f, 1.0f); | |
| 46 | |
| 47 private static final int POSITION_COORDINATE_DATA_SIZE = 3; | |
| 48 private static final int TEXTURE_COORDINATE_DATA_SIZE = 2; | |
| 49 | |
| 50 // Number of vertices passed to glDrawArrays(). | |
| 51 private static final int VERTICES_NUMBER = 6; | |
| 52 | |
| 53 // Threshold to determine whether to send the mouse move event. | |
| 54 private static final float CURSOR_MOVE_THRESHOLD = 1.0f; | |
| 55 | |
| 56 private final Client mClient; | |
| 57 private final Display mDisplay; | |
| 58 | |
| 59 private FloatBuffer mPositionCoordinates; | |
| 60 | |
| 61 private int mVertexShaderHandle; | |
| 62 private int mFragmentShaderHandle; | |
| 63 private int mProgramHandle; | |
| 64 private int mCombinedMatrixHandle; | |
| 65 private int mTextureUniformHandle; | |
| 66 private int mPositionHandle; | |
| 67 private int mTextureDataHandle; | |
| 68 private int mTextureCoordinateHandle; | |
| 69 | |
| 70 // Flag to indicate whether to reload the desktop texture. | |
| 71 private boolean mReloadTexture; | |
| 72 | |
| 73 // Lock to allow multithreaded access to mReloadTexture. | |
| 74 private final Object mReloadTextureLock = new Object(); | |
| 75 | |
| 76 private Bitmap mCursorBitmap; | |
| 77 | |
| 78 // Half width and half height of the cursor. | |
| 79 private PointF mHalfFrameSize; | |
| 80 | |
| 81 private PointF mCursorPosition; | |
| 82 | |
| 83 public Cursor(Client client, Display display) { | |
| 84 mClient = client; | |
| 85 mDisplay = display; | |
| 86 mHalfFrameSize = new PointF(0.0f, 0.0f); | |
| 87 mCursorPosition = new PointF(0.0f, 0.0f); | |
| 88 | |
| 89 mVertexShaderHandle = | |
| 90 ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADE
R); | |
| 91 mFragmentShaderHandle = | |
| 92 ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_S
HADER); | |
| 93 mProgramHandle = ShaderHelper.createAndLinkProgram(mVertexShaderHandle, | |
| 94 mFragmentShaderHandle, new String[] {"a_Position", "a_TexCoordin
ate", | |
| 95 "u_CombinedMatrix", "u_Texture"}); | |
| 96 | |
| 97 mCombinedMatrixHandle = | |
| 98 GLES20.glGetUniformLocation(mProgramHandle, "u_CombinedMatrix"); | |
| 99 mTextureUniformHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_T
exture"); | |
| 100 mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position
"); | |
| 101 mTextureDataHandle = TextureHelper.createTextureHandle(); | |
| 102 mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgramHandle, "a
_TexCoordinate"); | |
| 103 } | |
| 104 | |
| 105 /** | |
| 106 * Inform this object that a new cursor should be rendered. | |
| 107 * Called from native display thread. | |
| 108 */ | |
| 109 public void reloadTexture() { | |
| 110 synchronized (mReloadTextureLock) { | |
| 111 mReloadTexture = true; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 /** | |
| 116 * Return whether to move cursor. We only want to send the mouse move event | |
| 117 * when there is obvious change for cursor's location. | |
| 118 */ | |
| 119 private boolean moveCursor(PointF position) { | |
| 120 return Math.abs(mCursorPosition.x - position.x) > CURSOR_MOVE_THRESHOLD | |
| 121 || Math.abs(mCursorPosition.y - position.y) > CURSOR_MOVE_THRESH
OLD; | |
| 122 } | |
| 123 | |
| 124 /** | |
| 125 * Send the mouse move event to host when {@link moveCursor} returns true. | |
| 126 */ | |
| 127 public void moveTo(PointF position) { | |
| 128 if (moveCursor(position)) { | |
| 129 mClient.sendMouseEvent((int) position.x, (int) position.y, | |
| 130 InputStub.BUTTON_UNDEFINED, false); | |
| 131 } | |
| 132 mCursorPosition = position; | |
| 133 } | |
| 134 | |
| 135 /** | |
| 136 * Link the texture data for cursor if {@link mReloadTexture} is true. | |
| 137 * Invoked from {@link com.google.vrtoolkit.cardboard.CardboardView.StereoRe
nderer.onNewFrame} | |
| 138 */ | |
| 139 public void maybeLoadTexture(Desktop desktop) { | |
| 140 synchronized (mReloadTextureLock) { | |
| 141 if (!mReloadTexture || !desktop.hasVideoFrame()) { | |
| 142 return; | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 Bitmap cursorBitmap = mDisplay.getCursorBitmap(); | |
| 147 | |
| 148 if (cursorBitmap == mCursorBitmap) { | |
| 149 // Case when cursor image has not changed. | |
| 150 synchronized (mReloadTextureLock) { | |
| 151 mReloadTexture = false; | |
| 152 } | |
| 153 return; | |
| 154 } | |
| 155 | |
| 156 mCursorBitmap = cursorBitmap; | |
| 157 updatePosition(desktop, mCursorBitmap, mDisplay.getCursorHotspot()); | |
| 158 | |
| 159 TextureHelper.linkTexture(mTextureDataHandle, cursorBitmap); | |
| 160 | |
| 161 synchronized (mReloadTextureLock) { | |
| 162 mReloadTexture = false; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 public boolean hasImageFrame() { | |
| 167 return mCursorBitmap != null; | |
| 168 } | |
| 169 | |
| 170 /** | |
| 171 * Update the cursor position data if the cursor changes. | |
| 172 */ | |
| 173 private void updatePosition(Desktop desktop, Bitmap cursor, Point hotspot) { | |
| 174 Point desktopFrameSizePixels = desktop.getFrameSizePixels(); | |
| 175 float newHalfWidth = desktop.getHalfWidth() / desktopFrameSizePixels.x | |
| 176 * cursor.getWidth(); | |
| 177 float newHalfHeight = desktop.getHalfHeight() / desktopFrameSizePixels.y | |
| 178 * cursor.getHeight(); | |
| 179 | |
| 180 if (Math.abs(mHalfFrameSize.x - newHalfWidth) > 0.00001 | |
| 181 || Math.abs(mHalfFrameSize.y - newHalfHeight) > 0.00001) { | |
| 182 // Put the hotspot as the center of the cursor. | |
| 183 float hotspotX = (float) hotspot.x / desktopFrameSizePixels.x; | |
| 184 float hotspotY = (float) hotspot.y / desktopFrameSizePixels.y; | |
| 185 float left = -newHalfWidth + hotspotX; | |
| 186 float right = newHalfWidth + hotspotX; | |
| 187 float bottom = -newHalfHeight - hotspotY; | |
| 188 float top = newHalfHeight - hotspotY; | |
| 189 | |
| 190 mPositionCoordinates = makeFloatBuffer(new float[] { | |
| 191 // Desktop model coordinates. | |
| 192 left, top, 0.0f, | |
| 193 left, bottom, 0.0f, | |
| 194 right, top, 0.0f, | |
| 195 left, bottom, 0.0f, | |
| 196 right, bottom, 0.0f, | |
| 197 right, top, 0.0f | |
| 198 }); | |
| 199 | |
| 200 mHalfFrameSize = new PointF(newHalfWidth, newHalfHeight); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 /** | |
| 205 * Draw menu item according to the given model view projection matrix. | |
| 206 */ | |
| 207 public void draw(float[] combinedMatrix) { | |
| 208 GLES20.glUseProgram(mProgramHandle); | |
| 209 | |
| 210 GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT); | |
| 211 | |
| 212 // Pass in model view project matrix. | |
| 213 GLES20.glUniformMatrix4fv(mCombinedMatrixHandle, 1, false, combinedMatri
x, 0); | |
| 214 | |
| 215 // Pass in model position. | |
| 216 GLES20.glVertexAttribPointer(mPositionHandle, POSITION_COORDINATE_DATA_S
IZE, | |
| 217 GLES20.GL_FLOAT, false, 0, mPositionCoordinates); | |
| 218 GLES20.glEnableVertexAttribArray(mPositionHandle); | |
| 219 | |
| 220 // Pass in texture coordinate. | |
| 221 GLES20.glVertexAttribPointer(mTextureCoordinateHandle, TEXTURE_COORDINAT
E_DATA_SIZE, | |
| 222 GLES20.GL_FLOAT, false, 0, TEXTURE_COORDINATES); | |
| 223 GLES20.glEnableVertexAttribArray(mTextureCoordinateHandle); | |
| 224 | |
| 225 // Enable the transparent background. | |
| 226 GLES20.glEnable(GLES20.GL_BLEND); | |
| 227 GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA); | |
| 228 | |
| 229 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); | |
| 230 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle); | |
| 231 GLES20.glUniform1i(mTextureUniformHandle, 0); | |
| 232 | |
| 233 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, VERTICES_NUMBER); | |
| 234 | |
| 235 GLES20.glDisable(GLES20.GL_BLEND); | |
| 236 GLES20.glDisableVertexAttribArray(mPositionHandle); | |
| 237 GLES20.glDisableVertexAttribArray(mTextureCoordinateHandle); | |
| 238 } | |
| 239 | |
| 240 /* | |
| 241 * Clean cursor related opengl data. | |
| 242 */ | |
| 243 public void cleanup() { | |
| 244 GLES20.glDeleteShader(mVertexShaderHandle); | |
| 245 GLES20.glDeleteShader(mFragmentShaderHandle); | |
| 246 GLES20.glDeleteTextures(1, new int[] {mTextureDataHandle}, 0); | |
| 247 } | |
| 248 } | |
| OLD | NEW |