NativeImage开发指导 (C/C++)
场景介绍
NativeImage是提供Surface与OpenGL外部纹理相互绑定的模块,表示图形队列的消费者端。开发者可以通过NativeImage接口接收和使用Buffer,并将Buffer关联输出到绑定的OpenGL外部纹理。
NativeImage常见的开发场景如下:
- 通过
NativeImage提供的Native API接口创建NativeImage实例作为消费者端,获取与该实例对应的NativeWindow作为生产者端。NativeWindow相关接口可用于填充Buffer内容并提交,NativeImage将Buffer内容更新到OpenGL外部纹理上。本模块需要配合NativeWindow、NativeBuffer、EGL、GLES3模块一起使用。
接口说明
| 接口名 | 描述 |
|---|---|
| OH_NativeImage_Create (uint32_t textureId, uint32_t textureTarget) | 创建一个OH_NativeImage实例,该实例与OpenGL ES的纹理ID和纹理目标相关联。本接口需要与OH_NativeImage_Destroy接口配合使用,否则会存在内存泄露。 |
| OH_NativeImage_AcquireNativeWindow (OH_NativeImage *image) | 获取与OH_NativeImage相关联的OHNativeWindow指针,该OHNativeWindow在调用OH_NativeImage_Destroy时会将其释放,不需要调用OH_NativeWindow_DestroyNativeWindow释放,否则会出现访问已释放内存错误,可能会导致崩溃。 |
| OH_NativeImage_AttachContext (OH_NativeImage *image, uint32_t textureId) | 将OH_NativeImage实例附加到当前OpenGL ES上下文,且该OpenGL ES纹理会绑定到 GL_TEXTURE_EXTERNAL_OES,并通过OH_NativeImage进行更新。 |
| OH_NativeImage_DetachContext (OH_NativeImage *image) | 将OH_NativeImage实例从当前OpenGL ES上下文分离。 |
| OH_NativeImage_UpdateSurfaceImage (OH_NativeImage *image) | 通过OH_NativeImage获取最新帧更新相关联的OpenGL ES纹理。 |
| OH_NativeImage_GetTimestamp (OH_NativeImage *image) | 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的相关时间戳。 |
| OH_NativeImage_GetTransformMatrixV2 (OH_NativeImage *image, float matrix[16]) | 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的变化矩阵。 |
| OH_NativeImage_Destroy (OH_NativeImage **image) | 销毁通过OH_NativeImage_Create创建的OH_NativeImage实例,销毁后该OH_NativeImage指针会被赋值为空。 |
详细的接口说明请参考native_image。
开发步骤
以下步骤描述了如何使用NativeImage提供的Native API接口,创建OH_NativeImage实例作为消费者端,将数据内容更新到OpenGL外部纹理上。
添加动态链接库
CMakeLists.txt中添加以下库文件。
libEGL.so
libGLESv3.so
libnative_image.so
libnative_window.so
libnative_buffer.so
头文件
#include <iostream>
#include <string>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#include <sys/mman.h>
#include <native_image/native_image.h>
#include <native_window/external_window.h>
#include <native_buffer/native_buffer.h>
-
初始化EGL环境。
这里提供初始化EGL环境的代码示例。XComponent模块的详细使用方法,请参阅XComponent开发指导。
bool ImageRender::InitEGL(EGLNativeWindowType window, uint64_t width, uint64_t height) { window_ = window; width_ = width; height_ = height; if (!InitializeEGLDisplay() || !ChooseEGLConfig() || !CreateEGLContext() || !CreateEGLSurface()) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to initialize EGL"); return false; } if (!MakeCurrentContext()) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to make EGL context current"); return false; } if (!CompileAndLinkShaders()) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to compile and link shaders"); return false; } UpdateViewport(); return true; } // ··· bool ImageRender::InitializeEGLDisplay() { display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (display_ == EGL_NO_DISPLAY) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to get EGL display"); return false; } if (!eglInitialize(display_, nullptr, nullptr)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to initialize EGL"); return false; } return true; } bool ImageRender::ChooseEGLConfig() { const EGLint attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_NONE }; EGLint numConfigs; if (!eglChooseConfig(display_, attribs, &config_, 1, &numConfigs) || numConfigs == 0) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to choose EGL config"); return false; } return true; } bool ImageRender::CreateEGLContext() { const EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; context_ = eglCreateContext(display_, config_, EGL_NO_CONTEXT, contextAttribs); if (context_ == EGL_NO_CONTEXT) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to create EGL context"); return false; } return true; } bool ImageRender::CreateEGLSurface() { surface_ = eglCreateWindowSurface(display_, config_, window_, nullptr); if (surface_ == EGL_NO_SURFACE) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to create EGL surface"); return false; } return true; } bool ImageRender::MakeCurrentContext() { if (!eglMakeCurrent(display_, surface_, surface_, context_)) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Failed to make EGL context current"); return false; } return true; } void ImageRender::UpdateViewport() { glViewport(0, 0, static_cast<GLsizei>(width_), static_cast<GLsizei>(height_)); OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "ImageRender", "Viewport updated to %{public}llu x %{public}llu", width_, height_); } bool ImageRender::CompileAndLinkShaders() { GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, g_vertexShaderSource); if (vertexShader == 0) { return false; } GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, g_fragmentShaderSource); if (fragmentShader == 0) { glDeleteShader(vertexShader); return false; } shaderProgram_ = glCreateProgram(); glAttachShader(shaderProgram_, vertexShader); glAttachShader(shaderProgram_, fragmentShader); glLinkProgram(shaderProgram_); GLint linked; glGetProgramiv(shaderProgram_, GL_LINK_STATUS, &linked); if (!linked) { PrintProgramLinkError(shaderProgram_); glDeleteProgram(shaderProgram_); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return false; } glUseProgram(shaderProgram_); positionAttrib_ = glGetAttribLocation(shaderProgram_, "aPosition"); texCoordAttrib_ = glGetAttribLocation(shaderProgram_, "aTexCoord"); textureUniform_ = glGetUniformLocation(shaderProgram_, "uTexture"); glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return true; } void ImageRender::PrintProgramLinkError(GLuint program) { GLint infoLen = 0; glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen > 1) { std::unique_ptr<char[]> infoLog = std::make_unique<char[]>(infoLen); glGetProgramInfoLog(program, infoLen, nullptr, infoLog.get()); OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ImageRender", "Error linking program: %{public}s", infoLog.get()); } } -
创建OH_NativeImage实例。
glGenTextures(1, &nativeImageTexId_); // ··· nativeImage_ = OH_NativeImage_Create(nativeImageTexId_, GL_TEXTURE_EXTERNAL_OES); -
获取对应的数据生产者端NativeWindow。
nativeWindow_ = OH_NativeImage_AcquireNativeWindow(image); -
设置NativeWindow的宽高。
int32_t result = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow_, SET_BUFFER_GEOMETRY, static_cast<int32_t>(width_), static_cast<int32_t>(height_)); if (result != SUCCESS) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to set buffer geometry."); return false; } -
将生产的内容写入OHNativeWindowBuffer。
-
从NativeWindow中获取OHNativeWindowBuffer。
OHNativeWindowBuffer *buffer = nullptr; int releaseFenceFd = INVALID_FD; int32_t result = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer, &releaseFenceFd); if (result != SUCCESS || buffer == nullptr) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to request buffer, ret : %{public}d.", result); return; } // ··· BufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer); -
将生产的内容写入OHNativeWindowBuffer。
// 使用 mmap 获取虚拟地址 void *mappedAddr = mmap(nullptr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0); if (mappedAddr == MAP_FAILED) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to mmap buffer."); return; } // 获取像素指针 uint32_t *pixel = static_cast<uint32_t *>(mappedAddr); // 调用封装的函数来绘制渐变 DrawGradient(pixel, handle->stride / BYTES_PER_PIXEL, height_); // 解除内存映射 result = munmap(mappedAddr, handle->size); if (result == FAILURE) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to munmap buffer."); } // ··· void OHNativeRender::DrawGradient(uint32_t* pixel, uint64_t width, uint64_t height) { static double time = 0.0; time += ANIMATION_SPEED_INCREMENT; double offset = (sin(time) + MAX_INTENSITY) / INTENSITY_MULTIPLIER; // 箭头参数 const int arrowSize = std::min(width, height) / ARROW_SIZE_DIVISOR; const int arrowX = width / ARROW_SIZE_DIVISOR; const int arrowY = height / ARROW_SIZE_DIVISOR; const int stemWidth = arrowSize / STEM_WIDTH_DIVISOR; const int headWidth = arrowSize / HEAD_WIDTH_DIVISOR; const int headLength = arrowSize / HEAD_LENGTH_DIVISOR; const int stemStart = arrowX - arrowSize / ARROW_SIZE_DIVISOR; const int stemEnd = arrowX + arrowSize / ARROW_SIZE_DIVISOR - headLength; for (uint64_t y = 0; y < height; y++) { for (uint64_t x = 0; x < width; x++) { double normalizedX = static_cast<double>(x) / static_cast<double>(width - 1); bool isArrow = false; if ((x >= stemStart && x <= stemEnd && y >= arrowY - stemWidth * HEAD_SLOPE_MULTIPLIER && y <= arrowY + stemWidth * HEAD_SLOPE_MULTIPLIER) || (x >= stemEnd && x <= stemEnd + headLength && fabs(static_cast<int>(y - arrowY)) <= (headWidth * HEAD_SLOPE_MULTIPLIER) * (1.0 - static_cast<double>(x - stemEnd) / headLength))) { isArrow = true; } uint8_t red = static_cast<uint8_t>((1.0 - normalizedX) * MAX_COLOR_VALUE); uint8_t blue = static_cast<uint8_t>(normalizedX * MAX_COLOR_VALUE); uint8_t green = 0; uint8_t alpha = MAX_COLOR_VALUE; if (isArrow) { red = green = blue = MAX_COLOR_VALUE; } double intensity = fabs(normalizedX - offset); intensity = MAX_INTENSITY - std::min(INTENSITY_MULTIPLIER * intensity, INTENSITY_LIMIT); intensity = std::max(intensity, MIN_INTENSITY); red = static_cast<uint8_t>(red * intensity); green = static_cast<uint8_t>(green * intensity); blue = static_cast<uint8_t>(blue * intensity); *pixel++ = (static_cast<uint32_t>(alpha) << ALPHA_SHIFT) | (static_cast<uint32_t>(red) << RED_SHIFT) | (static_cast<uint32_t>(green) << GREEN_SHIFT) | (static_cast<uint32_t>(blue) << BLUE_SHIFT); } } } -
将OHNativeWindowBuffer提交到NativeWindow。
// 设置刷新区域 Region region{nullptr, 0}; // 提交给消费者 result = OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer, NO_FENCE, region); if (result != SUCCESS) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "OHNativeRender", "Failed to flush buffer, result : %{public}d.", result); }
-
-
更新内容到OpenGL纹理。
int32_t ret = OH_NativeImage_UpdateSurfaceImage(nativeImage_); if (ret != 0) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "OH_NativeImage_UpdateSurfaceImage failed, ret: %{public}d, texId: %{public}u", ret, nativeImageTexId_); return; } UpdateTextureMatrix(); if (imageRender_) { imageRender_->Render(); } else { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "ImageRender is null"); } // ··· void RenderEngine::UpdateTextureMatrix() { float matrix[16]; int32_t ret = OH_NativeImage_GetTransformMatrixV2(nativeImage_, matrix); if (ret != 0) { OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "RenderEngine", "OH_NativeImage_GetTransformMatrix failed, ret: %{public}d", ret); return; } imageRender_->SetTransformMatrix(matrix); } -
解绑OpenGL纹理,绑定到新的外部纹理上。
// 将OH_NativeImage实例从当前OpenGL ES上下文分离 OH_NativeImage_DetachContext(nativeImage_); // [Start nativeimage_create] glGenTextures(1, &nativeImageTexId_); // [StartExclude nativeimage_create] glBindTexture(GL_TEXTURE_EXTERNAL_OES, nativeImageTexId_); // ··· int ret = OH_NativeImage_AttachContext(nativeImage_, nativeImageTexId_); -
OH_NativeImage实例使用完需要销毁掉。
OH_NativeImage_Destroy(&nativeImage_);
相关实例
针对NativeImage的开发,有以下相关实例可供参考: