1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "image_render.h"
17 #include "common/common.h"
18 #include <GLES2/gl2ext.h>
19 #include <hilog/log.h>
20 #include <memory>
21 #include <cmath>
22 #include <algorithm>
23 #include <EGL/eglext.h>
24
25 namespace {
26 constexpr int MATRIX_SIZE = 16;
27 constexpr int MATRIX_DIAGONAL_STEP = 5;
28 constexpr float IDENTITY_DIAGONAL = 1.0f;
29 constexpr float IDENTITY_OTHER = 0.0f;
30 constexpr uint32_t NUM_VERTICES = 4;
31 constexpr uint32_t POSITION_COMPONENT_COUNT = 3;
32 constexpr uint32_t TEX_COORD_COMPONENT_COUNT = 2;
33 constexpr uint32_t STRIDE = (POSITION_COMPONENT_COUNT + TEX_COORD_COMPONENT_COUNT) * sizeof(GLfloat);
34 const char* g_vertexShaderSource = R"(
35 attribute vec4 aPosition;
36 attribute vec2 aTexCoord;
37 varying vec2 vTexCoord;
38 uniform mat4 uTransformMatrix;
39 void main() {
40 gl_Position = aPosition;
41 vTexCoord = (uTransformMatrix * vec4(aTexCoord, 0.0, 1.0)).xy;
42 }
43 )";
44
45 const char* g_fragmentShaderSource = R"(
46 #extension GL_OES_EGL_image_external : require
47 precision mediump float;
48 uniform samplerExternalOES uTexture;
49 varying vec2 vTexCoord;
50 void main() {
51 gl_FragColor = texture2D(uTexture, vTexCoord);
52 }
53 )";
54 }
55
ImageRender()56 ImageRender::ImageRender()
57 {
58 // Initialize transformMatrix_ as an identity matrix
59 for (int i = 0; i < MATRIX_SIZE; ++i) {
60 transformMatrix_[i] = (i % MATRIX_DIAGONAL_STEP == 0) ? IDENTITY_DIAGONAL : IDENTITY_OTHER;
61 }
62 }
63
~ImageRender()64 ImageRender::~ImageRender()
65 {
66 Cleanup();
67 }
68
InitEGL(EGLNativeWindowType window,uint64_t width,uint64_t height)69 bool ImageRender::InitEGL(EGLNativeWindowType window, uint64_t width, uint64_t height)
70 {
71 window_ = window;
72 width_ = width;
73 height_ = height;
74
75 if (!InitializeEGLDisplay() || !ChooseEGLConfig() || !CreateEGLContext() || !CreateEGLSurface()) {
76 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to initialize EGL");
77 return false;
78 }
79
80 if (!MakeCurrentContext()) {
81 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to make EGL context current");
82 return false;
83 }
84
85 if (!CompileAndLinkShaders()) {
86 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to compile and link shaders");
87 return false;
88 }
89
90 UpdateViewport();
91
92 return true;
93 }
94
UpdateWindowInfo(uint64_t width,uint64_t height)95 void ImageRender::UpdateWindowInfo(uint64_t width, uint64_t height)
96 {
97 width_ = width;
98 height_ = height;
99 }
100
InitializeEGLDisplay()101 bool ImageRender::InitializeEGLDisplay()
102 {
103 display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
104 if (display_ == EGL_NO_DISPLAY) {
105 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to get EGL display");
106 return false;
107 }
108
109 if (!eglInitialize(display_, nullptr, nullptr)) {
110 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to initialize EGL");
111 return false;
112 }
113
114 return true;
115 }
116
ChooseEGLConfig()117 bool ImageRender::ChooseEGLConfig()
118 {
119 const EGLint attribs[] = {
120 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
121 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
122 EGL_RED_SIZE, 8,
123 EGL_GREEN_SIZE, 8,
124 EGL_BLUE_SIZE, 8,
125 EGL_ALPHA_SIZE, 8,
126 EGL_NONE
127 };
128
129 EGLint numConfigs;
130 if (!eglChooseConfig(display_, attribs, &config_, 1, &numConfigs) || numConfigs == 0) {
131 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to choose EGL config");
132 return false;
133 }
134 return true;
135 }
136
CreateEGLContext()137 bool ImageRender::CreateEGLContext()
138 {
139 const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
140 context_ = eglCreateContext(display_, config_, EGL_NO_CONTEXT, contextAttribs);
141 if (context_ == EGL_NO_CONTEXT) {
142 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to create EGL context");
143 return false;
144 }
145 return true;
146 }
147
CreateEGLSurface()148 bool ImageRender::CreateEGLSurface()
149 {
150 surface_ = eglCreateWindowSurface(display_, config_, window_, nullptr);
151 if (surface_ == EGL_NO_SURFACE) {
152 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to create EGL surface");
153 return false;
154 }
155 return true;
156 }
157
MakeCurrentContext()158 bool ImageRender::MakeCurrentContext()
159 {
160 if (!eglMakeCurrent(display_, surface_, surface_, context_)) {
161 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to make EGL context current");
162 return false;
163 }
164 return true;
165 }
166
UpdateViewport()167 void ImageRender::UpdateViewport()
168 {
169 glViewport(0, 0, static_cast<GLsizei>(width_), static_cast<GLsizei>(height_));
170 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ImageRender",
171 "Viewport updated to %{public}llu x %{public}llu", width_, height_);
172 }
173
CompileAndLinkShaders()174 bool ImageRender::CompileAndLinkShaders()
175 {
176 GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, g_vertexShaderSource);
177 if (vertexShader == 0) {
178 return false;
179 }
180
181 GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, g_fragmentShaderSource);
182 if (fragmentShader == 0) {
183 glDeleteShader(vertexShader);
184 return false;
185 }
186
187 shaderProgram_ = glCreateProgram();
188 glAttachShader(shaderProgram_, vertexShader);
189 glAttachShader(shaderProgram_, fragmentShader);
190 glLinkProgram(shaderProgram_);
191
192 GLint linked;
193 glGetProgramiv(shaderProgram_, GL_LINK_STATUS, &linked);
194 if (!linked) {
195 PrintProgramLinkError(shaderProgram_);
196 glDeleteProgram(shaderProgram_);
197 glDeleteShader(vertexShader);
198 glDeleteShader(fragmentShader);
199 return false;
200 }
201
202 glUseProgram(shaderProgram_);
203
204 positionAttrib_ = glGetAttribLocation(shaderProgram_, "aPosition");
205 texCoordAttrib_ = glGetAttribLocation(shaderProgram_, "aTexCoord");
206 textureUniform_ = glGetUniformLocation(shaderProgram_, "uTexture");
207
208 glDeleteShader(vertexShader);
209 glDeleteShader(fragmentShader);
210
211 return true;
212 }
213
PrintProgramLinkError(GLuint program)214 void ImageRender::PrintProgramLinkError(GLuint program)
215 {
216 GLint infoLen = 0;
217 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
218 if (infoLen > 1) {
219 std::unique_ptr<char[]> infoLog = std::make_unique<char[]>(infoLen);
220 glGetProgramInfoLog(program, infoLen, nullptr, infoLog.get());
221 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN,
222 "ImageRender", "Error linking program: %{public}s", infoLog.get());
223 }
224 }
225
SetTransformMatrix(const float matrix[MATRIX_SIZE])226 void ImageRender::SetTransformMatrix(const float matrix[MATRIX_SIZE])
227 {
228 std::copy(matrix, matrix + MATRIX_SIZE, transformMatrix_);
229 }
230
SetTexture(GLuint textureId,GLuint textureTarget)231 void ImageRender::SetTexture(GLuint textureId, GLuint textureTarget)
232 {
233 textureId_ = textureId;
234 textureTarget_ = textureTarget;
235 }
236
SetupVertexAttributes()237 void ImageRender::SetupVertexAttributes()
238 {
239 static const GLfloat vertices[] = {
240 -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, // 左下
241 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, // 右下
242 -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // 左上
243 1.0f, 1.0f, 0.0f, 1.0f, 1.0f // 右上
244 };
245
246 // Enable and set the position attribute
247 glEnableVertexAttribArray(positionAttrib_);
248 glVertexAttribPointer(positionAttrib_, POSITION_COMPONENT_COUNT, GL_FLOAT, GL_FALSE,
249 STRIDE, vertices);
250
251 // Enable and set the texture coordinate attribute
252 glEnableVertexAttribArray(texCoordAttrib_);
253 glVertexAttribPointer(texCoordAttrib_, TEX_COORD_COMPONENT_COUNT, GL_FLOAT, GL_FALSE,
254 STRIDE, vertices + POSITION_COMPONENT_COUNT);
255 }
256
DisableVertexAttributes()257 void ImageRender::DisableVertexAttributes()
258 {
259 // Disable the vertex attribute arrays after rendering
260 glDisableVertexAttribArray(positionAttrib_);
261 glDisableVertexAttribArray(texCoordAttrib_);
262 }
263
Render()264 void ImageRender::Render()
265 {
266 if (surface_ == EGL_NO_SURFACE) {
267 return;
268 }
269
270 if (!eglMakeCurrent(display_, surface_, surface_, context_)) {
271 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to make context current in Render");
272 return;
273 }
274
275 // Clear the color buffer
276 glClearColor(0.0f, 1.0f, 1.0f, 1.0f);
277 glClear(GL_COLOR_BUFFER_BIT);
278
279 // Use the shader program
280 glUseProgram(shaderProgram_);
281
282 // Bind the texture
283 glActiveTexture(GL_TEXTURE0);
284 glBindTexture(textureTarget_, textureId_);
285
286 // Set the texture sampler to texture unit 0
287 glUniform1i(textureUniform_, 0);
288
289 // Set the transformation matrix
290 GLint matrixLocation = glGetUniformLocation(shaderProgram_, "uTransformMatrix");
291 if (matrixLocation != -1) {
292 glUniformMatrix4fv(matrixLocation, 1, GL_FALSE, transformMatrix_);
293 }
294
295 // Set up vertex attributes
296 SetupVertexAttributes();
297
298 // Draw the textured quad
299 glDrawArrays(GL_TRIANGLE_STRIP, 0, NUM_VERTICES);
300
301 // Disable vertex attributes
302 DisableVertexAttributes();
303
304 // Swap the buffers to display the rendered image
305 if (!eglSwapBuffers(display_, surface_)) {
306 EGLint error = eglGetError();
307 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender",
308 "eglSwapBuffers failed with error: %d", error);
309 }
310 }
311
Cleanup()312 void ImageRender::Cleanup()
313 {
314 if (display_ != EGL_NO_DISPLAY) {
315 eglMakeCurrent(display_, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
316
317 if (context_ != EGL_NO_CONTEXT) {
318 eglDestroyContext(display_, context_);
319 context_ = EGL_NO_CONTEXT;
320 }
321
322 if (surface_ != EGL_NO_SURFACE) {
323 eglDestroySurface(display_, surface_);
324 surface_ = EGL_NO_SURFACE;
325 }
326
327 eglTerminate(display_);
328 display_ = EGL_NO_DISPLAY;
329 }
330
331 if (shaderProgram_ != 0) {
332 glDeleteProgram(shaderProgram_);
333 shaderProgram_ = 0;
334 }
335 }
336
CompileShader(GLenum type,const char * source)337 GLuint ImageRender::CompileShader(GLenum type, const char* source)
338 {
339 GLuint shader = glCreateShader(type);
340 if (shader == 0) {
341 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to create shader.");
342 return 0;
343 }
344
345 glShaderSource(shader, 1, &source, nullptr);
346 glCompileShader(shader);
347
348 GLint compiled;
349 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
350 if (!compiled) {
351 PrintShaderCompileError(shader);
352 glDeleteShader(shader);
353 return 0;
354 }
355 return shader;
356 }
357
PrintShaderCompileError(GLuint shader)358 void ImageRender::PrintShaderCompileError(GLuint shader)
359 {
360 GLint infoLen = 0;
361 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
362 if (infoLen > 1) {
363 auto infoLog = std::make_unique<char[]>(infoLen);
364 glGetShaderInfoLog(shader, infoLen, nullptr, infoLog.get());
365 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender",
366 "Error compiling shader: %{public}s", infoLog.get());
367 }
368 }
369