1 /*
2  * Copyright (C) 2020 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.car.pm.blurredbackground;
18 
19 import android.graphics.Rect;
20 import android.opengl.GLES11Ext;
21 import android.opengl.GLES30;
22 
23 import java.nio.FloatBuffer;
24 import java.nio.IntBuffer;
25 
26 /**
27  * A class containing the OpenGL programs used to render a blurred texture
28  */
29 public class BlurTextureProgram {
30 
31     private static final float[] FRAME_COORDS = {
32             -1.0f, -1.0f,   // 0 bottom left
33             1.0f, -1.0f,    // 1 bottom right
34             -1.0f, 1.0f,    // 2 top left
35             1.0f, 1.0f,     // 3 top right
36     };
37     private static final float[] TEXTURE_COORDS = {
38             0.0f, 0.0f,     // 0 bottom left
39             1.0f, 0.0f,     // 1 bottom right
40             0.0f, 1.0f,     // 2 top left
41             1.0f, 1.0f      // 3 top right
42     };
43 
44     private static final float[] INVERTED_TEXTURE_COORDS = {
45             0.0f, 1.0f,     // 0 bottom left
46             1.0f, 1.0f,     // 1 bottom right
47             0.0f, 0.0f,     // 2 top left
48             1.0f, 0.0f      // 3 top right
49     };
50 
51     private static final int SIZEOF_FLOAT = 4;
52     private static final int NUM_COORDS_PER_VERTEX = 2;
53     private static final float BLUR_RADIUS = 40.0f;
54 
55     private final String mVertexShader;
56     private final String mHorizontalBlurShader;
57     private final String mVerticalBlurShader;
58 
59     private final int mHorizontalBlurProgram;
60     private final int mVerticalBlurProgram;
61 
62     private final int mWidth;
63     private final int mHeight;
64 
65     private final int mScreenshotTextureId;
66     private final IntBuffer mScreenshotTextureBuffer;
67     private final float[] mTexMatrix;
68     private final FloatBuffer mResolutionBuffer;
69 
70     private final FloatBuffer mVertexBuffer = GLHelper.createFloatBuffer(FRAME_COORDS);
71     private final FloatBuffer mTexBuffer = GLHelper.createFloatBuffer(TEXTURE_COORDS);
72     private final FloatBuffer mInvertedTexBuffer = GLHelper.createFloatBuffer(
73             INVERTED_TEXTURE_COORDS);
74 
75     // Locations of the uniforms and attributes for the horizontal program
76     private final int mUHorizontalMVPMatrixLoc;
77     private final int mUHorizontalTexMatrixLoc;
78     private final int mUHorizontalResolutionLoc;
79     private final int mUHorizontalRadiusLoc;
80     private final int mAHorizontalPositionLoc;
81     private final int mAHorizontalTextureCoordLoc;
82 
83     // Locations of the uniforms and attributes for the vertical program
84     private final int mUVerticalMVPMatrixLoc;
85     private final int mUVerticalTexMatrixLoc;
86     private final int mUVerticalResolutionLoc;
87     private final int mUVerticalRadiusLoc;
88     private final int mAVerticalPositionLoc;
89     private final int mAVerticalTextureCoordLoc;
90 
91     private final IntBuffer mFrameBuffer = IntBuffer.allocate(1);
92     private final IntBuffer mFirstPassTextureBuffer = IntBuffer.allocate(1);
93 
94     private int mFrameBufferId;
95     private int mFirstPassTextureId;
96 
97     /**
98      * Constructor for the BlurTextureProgram
99      *
100      * @param screenshotTextureBuffer IntBuffer
101      * @param texMatrix Float array used to scale the screenshot texture
102      * @param vertexShader String containing the horizontal blur shader
103      * @param horizontalBlurShader String containing the fragment shader for horizontal blur
104      * @param verticalBlurShader String containing the fragment shader for vertical blur
105      * @param windowRect Rect representing the location of the window being covered
106      */
BlurTextureProgram( IntBuffer screenshotTextureBuffer, float[] texMatrix, String vertexShader, String horizontalBlurShader, String verticalBlurShader, Rect windowRect )107     BlurTextureProgram(
108             IntBuffer screenshotTextureBuffer,
109             float[] texMatrix,
110             String vertexShader,
111             String horizontalBlurShader,
112             String verticalBlurShader,
113             Rect windowRect
114     ) {
115         mVertexShader = vertexShader;
116         mHorizontalBlurShader = horizontalBlurShader;
117         mVerticalBlurShader = verticalBlurShader;
118 
119         mScreenshotTextureBuffer = screenshotTextureBuffer;
120         mScreenshotTextureId = screenshotTextureBuffer.get(0);
121         mTexMatrix = texMatrix;
122 
123         mHorizontalBlurProgram = GLHelper.createProgram(mVertexShader, mHorizontalBlurShader);
124         mVerticalBlurProgram = GLHelper.createProgram(mVertexShader, mVerticalBlurShader);
125 
126         mWidth = windowRect.width();
127         mHeight = windowRect.height();
128 
129         mResolutionBuffer = FloatBuffer.wrap(new float[]{(float) mWidth, (float) mHeight, 1.0f});
130 
131         // Initialize the uniform and attribute locations for the horizontal blur program
132         mUHorizontalMVPMatrixLoc = GLES30.glGetUniformLocation(mHorizontalBlurProgram,
133                 "uMVPMatrix");
134         mUHorizontalTexMatrixLoc = GLES30.glGetUniformLocation(mHorizontalBlurProgram,
135                 "uTexMatrix");
136         mUHorizontalResolutionLoc = GLES30.glGetUniformLocation(mHorizontalBlurProgram,
137                 "uResolution");
138         mUHorizontalRadiusLoc = GLES30.glGetUniformLocation(mHorizontalBlurProgram, "uRadius");
139 
140         mAHorizontalPositionLoc = GLES30.glGetAttribLocation(mHorizontalBlurProgram, "aPosition");
141         mAHorizontalTextureCoordLoc = GLES30.glGetAttribLocation(mHorizontalBlurProgram,
142                 "aTextureCoord");
143 
144         // Initialize the uniform and attribute locations for the vertical blur program
145         mUVerticalMVPMatrixLoc = GLES30.glGetUniformLocation(mVerticalBlurProgram, "uMVPMatrix");
146         mUVerticalTexMatrixLoc = GLES30.glGetUniformLocation(mVerticalBlurProgram, "uTexMatrix");
147         mUVerticalResolutionLoc = GLES30.glGetUniformLocation(mVerticalBlurProgram, "uResolution");
148         mUVerticalRadiusLoc = GLES30.glGetUniformLocation(mVerticalBlurProgram, "uRadius");
149 
150         mAVerticalPositionLoc = GLES30.glGetAttribLocation(mVerticalBlurProgram, "aPosition");
151         mAVerticalTextureCoordLoc = GLES30.glGetAttribLocation(mVerticalBlurProgram,
152                 "aTextureCoord");
153     }
154 
155     /**
156      * Executes all of the rendering logic.  Sets up FrameBuffers and programs to complete
157      * two rendering passes on the captured screenshot to produce a blur.
158      */
render()159     public void render() {
160         setupProgram(mHorizontalBlurProgram, mScreenshotTextureId,
161                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
162         setHorizontalUniformsAndAttributes();
163 
164         // Create the framebuffer that will hold the texture we render to
165         // for the first shader pass
166         mFrameBufferId = GLHelper.createAndBindFramebuffer(mFrameBuffer);
167 
168         // Create the empty texture that will store the output of the first shader pass (this'll
169         // be held in the Framebuffer Object)
170         mFirstPassTextureId = GLHelper.createAndBindTextureObject(mFirstPassTextureBuffer,
171                 GLES30.GL_TEXTURE_2D);
172 
173         setupTextureForFramebuffer(mFirstPassTextureId);
174         assertValidFramebufferStatus();
175         renderToFramebuffer(mFrameBufferId);
176 
177         setupProgram(mVerticalBlurProgram, mFirstPassTextureId, GLES30.GL_TEXTURE_2D);
178         setVerticalUniformsAndAttributes();
179 
180         renderToSurface();
181         cleanupResources();
182     }
183 
184     /**
185      * Cleans up all OpenGL resources used by programs in this class
186      */
cleanupResources()187     public void cleanupResources() {
188         deleteFramebufferTexture();
189         deleteFrameBuffer();
190         deletePrograms();
191 
192         GLES30.glFlush();
193     }
194 
195     /**
196      * Attaches a 2D texture image to the active framebuffer object
197      *
198      * @param textureId The ID of the texture to be attached
199      */
setupTextureForFramebuffer(int textureId)200     private void setupTextureForFramebuffer(int textureId) {
201         GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGB, mWidth, mHeight, 0,
202                 GLES30.GL_RGB, GLES30.GL_UNSIGNED_BYTE, null);
203         GLHelper.checkGlErrors("glTexImage2D");
204         GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0,
205                 GLES30.GL_TEXTURE_2D, textureId, 0);
206         GLHelper.checkGlErrors("glFramebufferTexture2D");
207     }
208 
209     /**
210      * Deletes the texture stored in the framebuffer
211      */
deleteFramebufferTexture()212     private void deleteFramebufferTexture() {
213         GLES30.glDeleteTextures(mFirstPassTextureBuffer.capacity(), mFirstPassTextureBuffer);
214         GLHelper.checkGlErrors("glDeleteTextures");
215     }
216 
217     /**
218      * Deletes the frame buffers.
219      */
deleteFrameBuffer()220     private void deleteFrameBuffer() {
221         GLES30.glDeleteBuffers(1, mFrameBuffer);
222         GLHelper.checkGlErrors("glDeleteBuffers");
223     }
224 
225     /**
226      * Deletes the GL programs.
227      */
deletePrograms()228     private void deletePrograms() {
229         GLES30.glDeleteProgram(mHorizontalBlurProgram);
230         GLHelper.checkGlErrors("glDeleteProgram");
231         GLES30.glDeleteProgram(mVerticalBlurProgram);
232         GLHelper.checkGlErrors("glDeleteProgram");
233     }
234 
235     /**
236      * Set all of the Uniform and Attribute variable values for the horizontal blur program
237      */
setHorizontalUniformsAndAttributes()238     private void setHorizontalUniformsAndAttributes() {
239         GLES30.glUniformMatrix4fv(mUHorizontalMVPMatrixLoc, 1, false, GLHelper.getIdentityMatrix(),
240                 0);
241         GLES30.glUniformMatrix4fv(mUHorizontalTexMatrixLoc, 1, false, mTexMatrix, 0);
242         GLES30.glUniform3fv(mUHorizontalResolutionLoc, 1, mResolutionBuffer);
243         GLES30.glUniform1f(mUHorizontalRadiusLoc, BLUR_RADIUS);
244 
245         GLES30.glEnableVertexAttribArray(mAHorizontalPositionLoc);
246         GLES30.glVertexAttribPointer(mAHorizontalPositionLoc, NUM_COORDS_PER_VERTEX,
247                 GLES30.GL_FLOAT, false, NUM_COORDS_PER_VERTEX * SIZEOF_FLOAT, mVertexBuffer);
248 
249         GLES30.glEnableVertexAttribArray(mAHorizontalTextureCoordLoc);
250         GLES30.glVertexAttribPointer(mAHorizontalTextureCoordLoc, 2,
251                 GLES30.GL_FLOAT, false, 2 * SIZEOF_FLOAT, mTexBuffer);
252     }
253 
254     /**
255      * Set all of the Uniform and Attribute variable values for the vertical blur program
256      */
setVerticalUniformsAndAttributes()257     private void setVerticalUniformsAndAttributes() {
258         GLES30.glUniformMatrix4fv(mUVerticalMVPMatrixLoc, 1, false, GLHelper.getIdentityMatrix(),
259                 0);
260         GLES30.glUniformMatrix4fv(mUVerticalTexMatrixLoc, 1, false, mTexMatrix, 0);
261         GLES30.glUniform3fv(mUVerticalResolutionLoc, 1, mResolutionBuffer);
262         GLES30.glUniform1f(mUVerticalRadiusLoc, BLUR_RADIUS);
263 
264         GLES30.glEnableVertexAttribArray(mAVerticalPositionLoc);
265         GLES30.glVertexAttribPointer(mAVerticalPositionLoc, NUM_COORDS_PER_VERTEX,
266                 GLES30.GL_FLOAT, false, NUM_COORDS_PER_VERTEX * SIZEOF_FLOAT, mVertexBuffer);
267 
268         GLES30.glEnableVertexAttribArray(mAVerticalTextureCoordLoc);
269         GLES30.glVertexAttribPointer(mAVerticalTextureCoordLoc, 2,
270                 GLES30.GL_FLOAT, false, 2 * SIZEOF_FLOAT, mInvertedTexBuffer);
271     }
272 
273     /**
274      * Sets the program to be used in the next rendering, and binds
275      * a texture to it
276      *
277      * @param programId     The Id of the program
278      * @param textureId     The Id of the texture to be bound
279      * @param textureTarget The type of texture that is being bound
280      */
setupProgram(int programId, int textureId, int textureTarget)281     private void setupProgram(int programId, int textureId, int textureTarget) {
282         GLES30.glUseProgram(programId);
283         GLHelper.checkGlErrors("glUseProgram");
284 
285         GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
286         GLHelper.checkGlErrors("glActiveTexture");
287 
288         GLES30.glBindTexture(textureTarget, textureId);
289         GLHelper.checkGlErrors("glBindTexture");
290     }
291 
292     /**
293      * Renders to a framebuffer using the current active program
294      *
295      * @param framebufferId The Id of the framebuffer being rendered to
296      */
renderToFramebuffer(int framebufferId)297     private void renderToFramebuffer(int framebufferId) {
298         GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
299         GLHelper.checkGlErrors("glClear");
300 
301         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, framebufferId);
302         GLHelper.checkGlErrors("glBindFramebuffer");
303 
304         GLES30.glViewport(0, 0, mWidth, mHeight);
305         GLHelper.checkGlErrors("glViewport");
306 
307         GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0,
308                 FRAME_COORDS.length / NUM_COORDS_PER_VERTEX);
309         GLHelper.checkGlErrors("glDrawArrays");
310 
311         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
312     }
313 
314     /**
315      * Renders to a the GLSurface using the current active program
316      */
renderToSurface()317     private void renderToSurface() {
318         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0);
319         GLHelper.checkGlErrors("glDrawArrays");
320 
321         GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
322         GLHelper.checkGlErrors("glDrawArrays");
323 
324         GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0,
325                 FRAME_COORDS.length / NUM_COORDS_PER_VERTEX);
326         GLHelper.checkGlErrors("glDrawArrays");
327     }
328 
assertValidFramebufferStatus()329     private void assertValidFramebufferStatus() {
330         if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER)
331                 != GLES30.GL_FRAMEBUFFER_COMPLETE) {
332             throw new RuntimeException(
333                     "Failed to attach Framebuffer. Framebuffer status code is: "
334                             + GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER));
335         }
336     }
337 }
338