• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)