1# NativeImage开发指导 (C/C++) 2<!--Kit: ArkGraphics 2D--> 3<!--Subsystem: Graphics--> 4<!--Owner: @Felix-fangyang; @li_hui180; @dingpy--> 5<!--Designer: @conan13234--> 6<!--Tester: @nobuggers--> 7<!--Adviser: @ge-yafang--> 8## 场景介绍 9 10NativeImage是提供**Surface关联OpenGL外部纹理**的模块,表示图形队列的消费者端。开发者可以通过`NativeImage`接口接收和使用`Buffer`,并将`Buffer`关联输出到OpenGL外部纹理。 11针对NativeImage,常见的开发场景如下: 12 13* 通过`NativeImage`提供的Native API接口创建`NativeImage`实例作为消费者端,获取与该实例对应的`NativeWindow`作为生产者端。`NativeWindow`相关接口可用于填充`Buffer`内容并提交,`NativeImage`将`Buffer`内容更新到OpenGL外部纹理上。本模块需要配合NativeWindow、NativeBuffer、EGL、GLES3模块一起使用。 14 15## 接口说明 16 17| 接口名 | 描述 | 18| ------------------------------------------------------------ | ------------------------------------------------------------ | 19| OH_NativeImage_Create (uint32_t textureId, uint32_t textureTarget) | 创建一个OH_NativeImage实例,该实例与OpenGL ES的纹理ID和纹理目标相关联。本接口需要与OH_NativeImage_Destroy接口配合使用,否则会存在内存泄露。 | 20| OH_NativeImage_AcquireNativeWindow (OH_NativeImage \*image) | 获取与OH_NativeImage相关联的OHNativeWindow指针,该OHNativeWindow在调用OH_NativeImage_Destroy时会将其释放,不需要调用OH_NativeWindow_DestroyNativeWindow释放,否则会出现访问已释放内存错误,可能会导致崩溃。 | 21| OH_NativeImage_AttachContext (OH_NativeImage \*image, uint32_t textureId) | 将OH_NativeImage实例附加到当前OpenGL ES上下文,且该OpenGL ES纹理会绑定到 GL_TEXTURE_EXTERNAL_OES,并通过OH_NativeImage进行更新。 | 22| OH_NativeImage_DetachContext (OH_NativeImage \*image) | 将OH_NativeImage实例从当前OpenGL ES上下文分离。 | 23| OH_NativeImage_UpdateSurfaceImage (OH_NativeImage \*image) | 通过OH_NativeImage获取最新帧更新相关联的OpenGL ES纹理。 | 24| OH_NativeImage_GetTimestamp (OH_NativeImage \*image) | 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的相关时间戳。 | 25| OH_NativeImage_GetTransformMatrix (OH_NativeImage \*image, float matrix[16]) | 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的变化矩阵。 | 26| OH_NativeImage_Destroy (OH_NativeImage \*\*image) | 销毁通过OH_NativeImage_Create创建的OH_NativeImage实例,销毁后该OH_NativeImage指针会被赋值为空。 | 27 28详细的接口说明请参考[native_image](../reference/apis-arkgraphics2d/capi-oh-nativeimage.md)。 29 30## 开发步骤 31 32以下步骤描述了如何使用`NativeImage`提供的Native API接口,创建`OH_NativeImage`实例作为消费者端,将数据内容更新到OpenGL外部纹理上。 33 34**添加动态链接库** 35 36CMakeLists.txt中添加以下lib。 37 38```txt 39libEGL.so 40libGLESv3.so 41libnative_image.so 42libnative_window.so 43libnative_buffer.so 44``` 45 46**头文件** 47 48```c++ 49#include <iostream> 50#include <string> 51#include <EGL/egl.h> 52#include <EGL/eglext.h> 53#include <GLES3/gl3.h> 54#include <GLES2/gl2ext.h> 55#include <sys/mman.h> 56#include <native_image/native_image.h> 57#include <native_window/external_window.h> 58#include <native_buffer/native_buffer.h> 59``` 60 611. **初始化EGL环境**。 62 63 这里提供一份初始化EGL环境的代码示例。XComponent模块的具体使用方法请参考[XComponent开发指导](../ui/napi-xcomponent-guidelines.md)。 64 ```c++ 65 using GetPlatformDisplayExt = PFNEGLGETPLATFORMDISPLAYEXTPROC; 66 constexpr const char *EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland"; 67 constexpr const char *EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland"; 68 constexpr int32_t EGL_CONTEXT_CLIENT_VERSION_NUM = 2; 69 constexpr char CHARACTER_WHITESPACE = ' '; 70 constexpr const char *CHARACTER_STRING_WHITESPACE = " "; 71 constexpr const char *EGL_GET_PLATFORM_DISPLAY_EXT = "eglGetPlatformDisplayEXT"; 72 EGLContext eglContext_ = EGL_NO_CONTEXT; 73 EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; 74 static inline EGLConfig config_; 75 static inline EGLSurface eglSurface_; 76 // 从XComponent中获取到的OHNativeWindow 77 OHNativeWindow *eglNativeWindow_; 78 79 // 检查egl扩展 80 static bool CheckEglExtension(const char *extensions, const char *extension) { 81 size_t extlen = strlen(extension); 82 const char *end = extensions + strlen(extensions); 83 84 while (extensions < end) { 85 size_t n = 0; 86 if (*extensions == CHARACTER_WHITESPACE) { 87 extensions++; 88 continue; 89 } 90 n = strcspn(extensions, CHARACTER_STRING_WHITESPACE); 91 if (n == extlen && strncmp(extension, extensions, n) == 0) { 92 return true; 93 } 94 extensions += n; 95 } 96 return false; 97 } 98 99 // 获取当前的显示设备 100 static EGLDisplay GetPlatformEglDisplay(EGLenum platform, void *native_display, const EGLint *attrib_list) { 101 static GetPlatformDisplayExt eglGetPlatformDisplayExt = NULL; 102 103 if (!eglGetPlatformDisplayExt) { 104 const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); 105 if (extensions && (CheckEglExtension(extensions, EGL_EXT_PLATFORM_WAYLAND) || 106 CheckEglExtension(extensions, EGL_KHR_PLATFORM_WAYLAND))) { 107 eglGetPlatformDisplayExt = (GetPlatformDisplayExt)eglGetProcAddress(EGL_GET_PLATFORM_DISPLAY_EXT); 108 } 109 } 110 111 if (eglGetPlatformDisplayExt) { 112 return eglGetPlatformDisplayExt(platform, native_display, attrib_list); 113 } 114 115 return eglGetDisplay((EGLNativeDisplayType)native_display); 116 } 117 118 static void InitEGLEnv() { 119 // 获取当前的显示设备 120 eglDisplay_ = GetPlatformEglDisplay(EGL_PLATFORM_OHOS_KHR, EGL_DEFAULT_DISPLAY, NULL); 121 if (eglDisplay_ == EGL_NO_DISPLAY) { 122 std::cout << "Failed to create EGLDisplay gl errno : " << eglGetError() << std::endl; 123 } 124 125 EGLint major, minor; 126 // 初始化EGLDisplay 127 if (eglInitialize(eglDisplay_, &major, &minor) == EGL_FALSE) { 128 std::cout << "Failed to initialize EGLDisplay" << std::endl; 129 } 130 131 // 绑定图形绘制的API为OpenGLES 132 if (eglBindAPI(EGL_OPENGL_ES_API) == EGL_FALSE) { 133 std::cout << "Failed to bind OpenGL ES API" << std::endl; 134 } 135 136 unsigned int glRet; 137 EGLint count; 138 EGLint config_attribs[] = {EGL_SURFACE_TYPE, 139 EGL_WINDOW_BIT, 140 EGL_RED_SIZE, 141 8, 142 EGL_GREEN_SIZE, 143 8, 144 EGL_BLUE_SIZE, 145 8, 146 EGL_ALPHA_SIZE, 147 8, 148 EGL_RENDERABLE_TYPE, 149 EGL_OPENGL_ES3_BIT, 150 EGL_NONE}; 151 152 // 获取一个有效的系统配置信息 153 glRet = eglChooseConfig(eglDisplay_, config_attribs, &config_, 1, &count); 154 if (!(glRet && static_cast<unsigned int>(count) >= 1)) { 155 std::cout << "Failed to eglChooseConfig" << std::endl; 156 } 157 158 static const EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, EGL_CONTEXT_CLIENT_VERSION_NUM, EGL_NONE}; 159 160 // 创建上下文 161 eglContext_ = eglCreateContext(eglDisplay_, config_, EGL_NO_CONTEXT, context_attribs); 162 if (eglContext_ == EGL_NO_CONTEXT) { 163 std::cout << "Failed to create egl context, error:" << eglGetError() << std::endl; 164 } 165 166 // 创建eglSurface 167 eglSurface_ = eglCreateWindowSurface(eglDisplay_, config_, reinterpret_cast<EGLNativeWindowType>(eglNativeWindow_), context_attribs); 168 if (eglSurface_ == EGL_NO_SURFACE) { 169 std::cout << "Failed to create egl surface, error:" << eglGetError() << std::endl; 170 } 171 172 // 关联上下文 173 eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_); 174 175 // EGL环境初始化完成 176 std::cout << "Create EGL context successfully, version" << major << "." << minor << std::endl; 177 } 178 ``` 179 1802. **创建OH_NativeImage实例**。 181 182 ```c++ 183 // 创建 OpenGL 纹理 184 GLuint textureId; 185 glGenTextures(1, &textureId); 186 // 创建 NativeImage 实例,关联 OpenGL 纹理 187 OH_NativeImage* image = OH_NativeImage_Create(textureId, GL_TEXTURE_EXTERNAL_OES); 188 ``` 189 1903. **获取对应的数据生产者端NativeWindow**。 191 192 ```c++ 193 // 获取生产者NativeWindow 194 OHNativeWindow* nativeWindow = OH_NativeImage_AcquireNativeWindow(image); 195 ``` 196 1974. **设置NativeWindow的宽高**。 198 199 ```c++ 200 int code = SET_BUFFER_GEOMETRY; 201 int32_t width = 800; 202 int32_t height = 600; 203 int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, width, height); 204 ``` 205 2065. **将生产的内容写入OHNativeWindowBuffer**。 207 208 1. 从NativeWindow中获取OHNativeWindowBuffer。 209 210 ```c++ 211 OHNativeWindowBuffer *buffer = nullptr; 212 int fenceFd; 213 // 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 OHNativeWindowBuffer 实例 214 OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd); 215 216 BufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer); 217 ``` 218 219 2. 将生产的内容写入OHNativeWindowBuffer。 220 221 ```c++ 222 // 使用系统mmap接口拿到bufferHandle的内存虚拟地址 223 void *mappedAddr = mmap(handle->virAddr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0); 224 if (mappedAddr == MAP_FAILED) { 225 // mmap failed 226 } 227 static uint32_t value = 0x00; 228 value++; 229 uint32_t *pixel = static_cast<uint32_t *>(mappedAddr); 230 for (uint32_t x = 0; x < width; x++) { 231 for (uint32_t y = 0; y < height; y++) { 232 *pixel++ = value; 233 } 234 } 235 // 内存使用完记得去掉内存映射 236 int result = munmap(mappedAddr, handle->size); 237 if (result == -1) { 238 // munmap failed 239 } 240 ``` 241 242 3. 将OHNativeWindowBuffer提交到NativeWindow。 243 244 ```c++ 245 // 设置刷新区域,如果Region中的Rect数组为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部内容有更改。 246 Region region{nullptr, 0}; 247 // 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。 248 OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region); 249 ``` 250 251 4. 用完需要销毁NativeWindow。 252 253 ```c++ 254 OH_NativeWindow_DestroyNativeWindow(nativeWindow); 255 ``` 256 2576. **更新内容到OpenGL纹理**。 258 259 ```c++ 260 // 更新内容到OpenGL纹理。 261 ret = OH_NativeImage_UpdateSurfaceImage(image); 262 if (ret != 0) { 263 std::cout << "OH_NativeImage_UpdateSurfaceImage failed" << std::endl; 264 } 265 // 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的时间戳和变化矩阵。 266 int64_t timeStamp = OH_NativeImage_GetTimestamp(image); 267 float matrix[16]; 268 ret = OH_NativeImage_GetTransformMatrix(image, matrix); 269 if (ret != 0) { 270 std::cout << "OH_NativeImage_GetTransformMatrix failed" << std::endl; 271 } 272 273 // 对update绑定到对应textureId的纹理做对应的opengl后处理后,将纹理上屏 274 EGLBoolean eglRet = eglSwapBuffers(eglDisplay_, eglSurface_); 275 if (eglRet == EGL_FALSE) { 276 std::cout << "eglSwapBuffers failed" << std::endl; 277 } 278 ``` 279 2807. **解绑OpenGL纹理,绑定到新的外部纹理上**。 281 282 ```c++ 283 // 将OH_NativeImage实例从当前OpenGL ES上下文分离 284 ret = OH_NativeImage_DetachContext(image); 285 if (ret != 0) { 286 std::cout << "OH_NativeImage_DetachContext failed" << std::endl; 287 } 288 // 将OH_NativeImage实例附加到当前OpenGL ES上下文, 且该OpenGL ES纹理会绑定到 GL_TEXTURE_EXTERNAL_OES, 并通过OH_NativeImage进行更新 289 GLuint textureId2; 290 glGenTextures(1, &textureId2); 291 ret = OH_NativeImage_AttachContext(image, textureId2); 292 ``` 293 2948. **OH_NativeImage实例使用完需要销毁掉**。 295 296 ```c++ 297 // 销毁OH_NativeImage实例 298 OH_NativeImage_Destroy(&image); 299 ``` 300 301## 相关实例 302 303针对NativeImage的开发,有以下相关实例可供参考: 304 305- [Native Window(API11)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkNativeWindow) 306- [基于NdkNativeImage的平滑渐变动画效果(API12)](https://gitcode.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkNativeImage)