1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.glwallpaper; 18 19 import static android.opengl.GLES20.GL_FLOAT; 20 import static android.opengl.GLES20.GL_LINEAR; 21 import static android.opengl.GLES20.GL_TEXTURE0; 22 import static android.opengl.GLES20.GL_TEXTURE_2D; 23 import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER; 24 import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER; 25 import static android.opengl.GLES20.GL_TRIANGLES; 26 import static android.opengl.GLES20.glActiveTexture; 27 import static android.opengl.GLES20.glBindTexture; 28 import static android.opengl.GLES20.glDrawArrays; 29 import static android.opengl.GLES20.glEnableVertexAttribArray; 30 import static android.opengl.GLES20.glGenTextures; 31 import static android.opengl.GLES20.glTexParameteri; 32 import static android.opengl.GLES20.glUniform1i; 33 import static android.opengl.GLES20.glVertexAttribPointer; 34 35 import android.graphics.Bitmap; 36 import android.graphics.Rect; 37 import android.opengl.GLUtils; 38 import android.util.Log; 39 40 import java.io.FileDescriptor; 41 import java.io.PrintWriter; 42 import java.nio.ByteBuffer; 43 import java.nio.ByteOrder; 44 import java.nio.FloatBuffer; 45 46 /** 47 * This class takes charge of the geometry data like vertices and texture coordinates. 48 * It delivers these data to opengl runtime and triggers draw calls if necessary. 49 */ 50 class ImageGLWallpaper { 51 private static final String TAG = ImageGLWallpaper.class.getSimpleName(); 52 53 static final String A_POSITION = "aPosition"; 54 static final String A_TEXTURE_COORDINATES = "aTextureCoordinates"; 55 static final String U_PER85 = "uPer85"; 56 static final String U_REVEAL = "uReveal"; 57 static final String U_AOD2OPACITY = "uAod2Opacity"; 58 static final String U_TEXTURE = "uTexture"; 59 60 private static final int HANDLE_UNDEFINED = -1; 61 private static final int POSITION_COMPONENT_COUNT = 2; 62 private static final int TEXTURE_COMPONENT_COUNT = 2; 63 private static final int BYTES_PER_FLOAT = 4; 64 65 // Vertices to define the square with 2 triangles. 66 private static final float[] VERTICES = { 67 -1.0f, -1.0f, 68 +1.0f, -1.0f, 69 +1.0f, +1.0f, 70 +1.0f, +1.0f, 71 -1.0f, +1.0f, 72 -1.0f, -1.0f 73 }; 74 75 // Texture coordinates that maps to vertices. 76 private static final float[] TEXTURES = { 77 0f, 1f, 78 1f, 1f, 79 1f, 0f, 80 1f, 0f, 81 0f, 0f, 82 0f, 1f 83 }; 84 85 private final FloatBuffer mVertexBuffer; 86 private final FloatBuffer mTextureBuffer; 87 private final ImageGLProgram mProgram; 88 89 private int mAttrPosition; 90 private int mAttrTextureCoordinates; 91 private int mUniAod2Opacity; 92 private int mUniPer85; 93 private int mUniReveal; 94 private int mUniTexture; 95 private int mTextureId; 96 97 private float[] mCurrentTexCoordinate; 98 99 ImageGLWallpaper(ImageGLProgram program) { 100 mProgram = program; 101 102 // Create an float array in opengles runtime (native) and put vertex data. 103 mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT) 104 .order(ByteOrder.nativeOrder()) 105 .asFloatBuffer(); 106 mVertexBuffer.put(VERTICES); 107 mVertexBuffer.position(0); 108 109 // Create an float array in opengles runtime (native) and put texture data. 110 mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT) 111 .order(ByteOrder.nativeOrder()) 112 .asFloatBuffer(); 113 mTextureBuffer.put(TEXTURES); 114 mTextureBuffer.position(0); 115 } 116 117 void setup(Bitmap bitmap) { 118 setupAttributes(); 119 setupUniforms(); 120 setupTexture(bitmap); 121 } 122 123 private void setupAttributes() { 124 mAttrPosition = mProgram.getAttributeHandle(A_POSITION); 125 mVertexBuffer.position(0); 126 glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT, 127 false, 0, mVertexBuffer); 128 glEnableVertexAttribArray(mAttrPosition); 129 130 mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES); 131 mTextureBuffer.position(0); 132 glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT, 133 false, 0, mTextureBuffer); 134 glEnableVertexAttribArray(mAttrTextureCoordinates); 135 } 136 137 private void setupUniforms() { 138 mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY); 139 mUniPer85 = mProgram.getUniformHandle(U_PER85); 140 mUniReveal = mProgram.getUniformHandle(U_REVEAL); 141 mUniTexture = mProgram.getUniformHandle(U_TEXTURE); 142 } 143 144 int getHandle(String name) { 145 switch (name) { 146 case A_POSITION: 147 return mAttrPosition; 148 case A_TEXTURE_COORDINATES: 149 return mAttrTextureCoordinates; 150 case U_AOD2OPACITY: 151 return mUniAod2Opacity; 152 case U_PER85: 153 return mUniPer85; 154 case U_REVEAL: 155 return mUniReveal; 156 case U_TEXTURE: 157 return mUniTexture; 158 default: 159 return HANDLE_UNDEFINED; 160 } 161 } 162 163 void draw() { 164 glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2); 165 } 166 167 private void setupTexture(Bitmap bitmap) { 168 final int[] tids = new int[1]; 169 170 if (bitmap == null) { 171 Log.w(TAG, "setupTexture: invalid bitmap"); 172 return; 173 } 174 175 // Generate one texture object and store the id in tids[0]. 176 glGenTextures(1, tids, 0); 177 if (tids[0] == 0) { 178 Log.w(TAG, "setupTexture: glGenTextures() failed"); 179 return; 180 } 181 182 // Bind a named texture to a target. 183 glBindTexture(GL_TEXTURE_2D, tids[0]); 184 // Load the bitmap data and copy it over into the texture object that is currently bound. 185 GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); 186 // Use bilinear texture filtering when minification. 187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 188 // Use bilinear texture filtering when magnification. 189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 190 191 mTextureId = tids[0]; 192 } 193 194 void useTexture() { 195 // Set the active texture unit to texture unit 0. 196 glActiveTexture(GL_TEXTURE0); 197 // Bind the texture to this unit. 198 glBindTexture(GL_TEXTURE_2D, mTextureId); 199 // Let the texture sampler in fragment shader to read form this texture unit. 200 glUniform1i(mUniTexture, 0); 201 } 202 203 /** 204 * This method adjust s(x-axis), t(y-axis) texture coordinates to get current display area 205 * of texture and will be used during transition. 206 * The adjustment happens if either the width or height of the surface is larger than 207 * corresponding size of the display area. 208 * If both width and height are larger than corresponding size of the display area, 209 * the adjustment will happen at both s, t side. 210 * 211 * @param surface The size of the surface. 212 * @param scissor The display area. 213 * @param xOffset The offset amount along s axis. 214 * @param yOffset The offset amount along t axis. 215 */ 216 void adjustTextureCoordinates(Rect surface, Rect scissor, float xOffset, float yOffset) { 217 mCurrentTexCoordinate = TEXTURES.clone(); 218 219 if (surface == null || scissor == null) { 220 mTextureBuffer.put(mCurrentTexCoordinate); 221 mTextureBuffer.position(0); 222 return; 223 } 224 225 int surfaceWidth = surface.width(); 226 int surfaceHeight = surface.height(); 227 int scissorWidth = scissor.width(); 228 int scissorHeight = scissor.height(); 229 230 if (surfaceWidth > scissorWidth) { 231 // Calculate the new s pos in pixels. 232 float pixelS = (float) Math.round((surfaceWidth - scissorWidth) * xOffset); 233 // Calculate the s pos in texture coordinate. 234 float coordinateS = pixelS / surfaceWidth; 235 // Calculate the percentage occupied by the scissor width in surface width. 236 float surfacePercentageW = (float) scissorWidth / surfaceWidth; 237 // Need also consider the case if surface height is smaller than scissor height. 238 if (surfaceHeight < scissorHeight) { 239 // We will narrow the surface percentage to keep aspect ratio. 240 surfacePercentageW *= (float) surfaceHeight / scissorHeight; 241 } 242 // Determine the final s pos, also limit the legal s pos to prevent from out of range. 243 float s = coordinateS + surfacePercentageW > 1f ? 1f - surfacePercentageW : coordinateS; 244 // Traverse the s pos in texture coordinates array and adjust the s pos accordingly. 245 for (int i = 0; i < mCurrentTexCoordinate.length; i += 2) { 246 // indices 2, 4 and 6 are the end of s coordinates. 247 if (i == 2 || i == 4 || i == 6) { 248 mCurrentTexCoordinate[i] = Math.min(1f, s + surfacePercentageW); 249 } else { 250 mCurrentTexCoordinate[i] = s; 251 } 252 } 253 } 254 255 if (surfaceHeight > scissorHeight) { 256 // Calculate the new t pos in pixels. 257 float pixelT = (float) Math.round((surfaceHeight - scissorHeight) * yOffset); 258 // Calculate the t pos in texture coordinate. 259 float coordinateT = pixelT / surfaceHeight; 260 // Calculate the percentage occupied by the scissor height in surface height. 261 float surfacePercentageH = (float) scissorHeight / surfaceHeight; 262 // Need also consider the case if surface width is smaller than scissor width. 263 if (surfaceWidth < scissorWidth) { 264 // We will narrow the surface percentage to keep aspect ratio. 265 surfacePercentageH *= (float) surfaceWidth / scissorWidth; 266 } 267 // Determine the final t pos, also limit the legal t pos to prevent from out of range. 268 float t = coordinateT + surfacePercentageH > 1f ? 1f - surfacePercentageH : coordinateT; 269 // Traverse the t pos in texture coordinates array and adjust the t pos accordingly. 270 for (int i = 1; i < mCurrentTexCoordinate.length; i += 2) { 271 // indices 1, 3 and 11 are the end of t coordinates. 272 if (i == 1 || i == 3 || i == 11) { 273 mCurrentTexCoordinate[i] = Math.min(1f, t + surfacePercentageH); 274 } else { 275 mCurrentTexCoordinate[i] = t; 276 } 277 } 278 } 279 280 mTextureBuffer.put(mCurrentTexCoordinate); 281 mTextureBuffer.position(0); 282 } 283 284 /** 285 * Called to dump current state. 286 * @param prefix prefix. 287 * @param fd fd. 288 * @param out out. 289 * @param args args. 290 */ 291 public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) { 292 StringBuilder sb = new StringBuilder(); 293 sb.append('{'); 294 if (mCurrentTexCoordinate != null) { 295 for (int i = 0; i < mCurrentTexCoordinate.length; i++) { 296 sb.append(mCurrentTexCoordinate[i]).append(','); 297 if (i == mCurrentTexCoordinate.length - 1) { 298 sb.deleteCharAt(sb.length() - 1); 299 } 300 } 301 } 302 sb.append('}'); 303 out.print(prefix); out.print("mTexCoordinates="); out.println(sb.toString()); 304 } 305 } 306