1 /*
<lambda>null2  * Copyright 2022 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 androidx.graphics.lowlatency
18 
19 import android.graphics.Color
20 import android.opengl.GLES20
21 import java.nio.ByteBuffer
22 import java.nio.ByteOrder
23 import java.nio.FloatBuffer
24 
25 /** OpenGL Renderer class responsible for drawing lines */
26 class LineRenderer {
27 
28     private var mVertexShader: Int = -1
29     private var mFragmentShader: Int = -1
30     private var mGlProgram: Int = -1
31 
32     private var mPositionHandle: Int = -1
33     private var mMvpMatrixHandle: Int = -1
34 
35     private var mColorHandle: Int = -1
36     private val mColorArray = FloatArray(4)
37 
38     private var mVertexBuffer: FloatBuffer? = null
39     private val mLineCoords = FloatArray(6)
40 
41     fun initialize() {
42         release()
43         GLES20.glEnable(GLES20.GL_BLEND)
44         GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA)
45         mVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VertexShaderCode)
46         mFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FragmentShaderCode)
47 
48         mGlProgram = GLES20.glCreateProgram()
49 
50         GLES20.glAttachShader(mGlProgram, mVertexShader)
51         GLES20.glAttachShader(mGlProgram, mFragmentShader)
52 
53         GLES20.glLinkProgram(mGlProgram)
54 
55         val bb: ByteBuffer =
56             ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float)
57                 LineCoordsSize * 4
58             )
59         // use the device hardware's native byte order
60         bb.order(ByteOrder.nativeOrder())
61 
62         // create a floating point buffer from the ByteBuffer
63         mVertexBuffer =
64             bb.asFloatBuffer().apply {
65                 put(mLineCoords)
66                 position(0)
67             }
68 
69         mPositionHandle = GLES20.glGetAttribLocation(mGlProgram, vPosition)
70         mMvpMatrixHandle = GLES20.glGetUniformLocation(mGlProgram, uMVPMatrix)
71         mColorHandle = GLES20.glGetUniformLocation(mGlProgram, vColor)
72     }
73 
74     fun release() {
75         if (mVertexShader != -1) {
76             GLES20.glDeleteShader(mVertexShader)
77             mVertexShader = -1
78         }
79 
80         if (mFragmentShader != -1) {
81             GLES20.glDeleteShader(mFragmentShader)
82             mFragmentShader = -1
83         }
84 
85         if (mGlProgram != -1) {
86             GLES20.glDeleteProgram(mGlProgram)
87             mGlProgram = -1
88         }
89     }
90 
91     fun drawLines(
92         mvpMatrix: FloatArray,
93         lines: FloatArray,
94         color: Int = Color.RED,
95         lineWidth: Float = 10f
96     ) {
97         GLES20.glUseProgram(mGlProgram)
98         GLES20.glLineWidth(lineWidth)
99         GLES20.glEnableVertexAttribArray(mPositionHandle)
100         mColorArray[0] = Color.red(color) / 255f
101         mColorArray[1] = Color.green(color) / 255f
102         mColorArray[2] = Color.blue(color) / 255f
103         mColorArray[3] = Color.alpha(color) / 255f
104         // Set color for drawing the triangle
105         GLES20.glUniform4fv(mColorHandle, 1, mColorArray, 0)
106         GLES20.glUniformMatrix4fv(mMvpMatrixHandle, 1, false, mvpMatrix, 0)
107 
108         mVertexBuffer?.let { buffer ->
109             for (i in 0 until lines.size step 4) {
110                 mLineCoords[0] = lines[i]
111                 mLineCoords[1] = lines[i + 1]
112                 mLineCoords[2] = 0f
113                 mLineCoords[3] = lines[i + 2]
114                 mLineCoords[4] = lines[i + 3]
115                 mLineCoords[5] = 0f
116                 buffer.put(mLineCoords)
117                 buffer.position(0)
118             }
119 
120             // Prepare the triangle coordinate data
121             GLES20.glVertexAttribPointer(
122                 mPositionHandle,
123                 CoordsPerVertex,
124                 GLES20.GL_FLOAT,
125                 false,
126                 VertexStride,
127                 buffer
128             )
129             GLES20.glDrawArrays(GLES20.GL_LINES, 0, VertexCount)
130         }
131         GLES20.glDisableVertexAttribArray(mPositionHandle)
132     }
133 
134     companion object {
135 
136         const val CoordsPerVertex = 3
137         const val LineCoordsSize = 6
138         private val VertexCount: Int = LineCoordsSize / CoordsPerVertex
139         private val VertexStride: Int = CoordsPerVertex * 4 // 4 bytes per vertex
140 
141         private const val uMVPMatrix = "uMVPMatrix"
142         private const val vPosition = "vPosition"
143         private const val VertexShaderCode =
144             """
145                 uniform mat4 $uMVPMatrix;
146                 attribute vec4 $vPosition;
147                 void main() { // the matrix must be included as a modifier of gl_Position
148                   gl_Position = $uMVPMatrix * $vPosition;
149                 }
150             """
151         private const val vColor = "vColor"
152         private const val FragmentShaderCode =
153             """
154                 precision highp float;
155 
156                 uniform vec4 $vColor;
157                 void main() {
158                     gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
159                     gl_FragColor = $vColor;
160                 }
161             """
162     }
163 }
164