OpenHarmony开发者论坛

标题: OpenHarmony应用开发引入开源C/C++库---之EGL Render [打印本页]

作者: 深开鸿_王石    时间: 2024-4-16 18:00
标题: OpenHarmony应用开发引入开源C/C++库---之EGL Render
[md]### 前言

[napi\_generator: NAPI框架生成工具 - Gitee.com](https://gitee.com/openharmony/na ... mples/napitutorials)

**NAPI仓旨在为应用开发者提供更多的NativeC++接口和使用C/C++库开发的应用方式,我们这个系列会逐步介绍EGL,FFMPEG,OpenCV等图形图像处理的能力和应用开发。如果有其他三方库的需求和应用呈现也可以在仓里提issue留言。**

### XComponent

##### 定义

**Native XComponent是XComponent组件提供在Native层的实例,可作为JS层和Native层XComponent绑定的桥梁。XComponent所提供的的NDK接口都依赖于该实例。接口能力包括获取Native Window实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下:**

* **利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。**
* **在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。**
* **利用Native Window和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列**

##### 接口

**XComponent(value: {id: string, type: string, libraryname?: string, controller?: XComponentController})**

**参数:**

| **参数名**      | **参数类型**                                                                                                                                                             | **必填** | **描述**                                                                                                                                                                                                                                                      |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **id**          | **string**                                                                                                                                                               | **是**   | **组件的唯一标识,支持最大的字符串长度128。**                                                                                                                                                                                                                 |
| **type**        | **string**                                                                                                                                                               | **是**   | **用于指定XComponent组件类型,可选值仅有两个为: -"surface":用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。 -"component"9+ :XComponent将变成一个容器组件,并可在其中执行非UI逻辑以动态加载显示内容。 其他值均会被视为"surface"类型** |
| **libraryname** | **string**                                                                                                                                                               | **否**   | **应用Native层编译输出动态库名称,仅XComponent类型为"surface"时有效。**                                                                                                                                                                                       |
| **controller**  | [XComponentcontroller](https://gitee.com/openharmony/do ... componentcontroller) | **否**   | **给组件绑定一个控制器,通过控制器调用组件方法,仅XComponent类型为"surface"时有效。**                                                                                                                                                                         |

##### XComponent10+

**XComponent(value: {id: string, type: XComponentType, libraryname?: string, controller?: XComponentController})**

**参数:**

| **参数名**      | **参数类型**                                                                                                                                                                                       | **必填** | **描述**                                                                           |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------- |
| **id**          | **string**                                                                                                                                                                                         | **是**   | **组件的唯一标识,支持最大的字符串长度128。**                                      |
| **type**        | [XComponentType](https://gitee.com/openharmony/do ... E%E8%AF%B4%E6%98%8E) | **是**   | **用于指定XComponent组件类型。**                                                   |
| **libraryname** | **string**                                                                                                                                                                                         | **否**   | **用Native层编译输出动态库名称,仅类型为SURFACE或TEXTURE时有效。**                 |
| **controller**  | [XComponentcontroller](https://gitee.com/openharmony/do ... componentcontroller)                           | **否**   | **给组件绑定一个控制器,通过控制器调用组件方法,仅类型为SURFACE或TEXTURE时有效。** |

##### XComponentType10+枚举说明

| **名称**      | **描述**                                                                                             |
| ------------------- | ---------------------------------------------------------------------------------------------------------- |
| **SURFACE**   | **用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。**                           |
| **COMPONENT** | **XComponent将变成一个容器组件,并可在其中执行非UI逻辑以动态加载显示内容。**                         |
| **TEXTURE**   | **用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容会和XComponent组件的内容合成后展示到屏幕上。** |

> **说明:**
>
> **type为COMPONENT("component")时,XComponent作为容器,子组件沿垂直方向布局:**
>
> * **垂直方向上对齐格式:**[FlexAlign](https://gitee.com/openharmony/do ... -enums.md#flexalign).Start
> * **水平方向上对齐格式:**[FlexAlign](https://gitee.com/openharmony/do ... -enums.md#flexalign).Center
>
> **所有的事件响应均不支持。**
>
> **布局方式更改和事件响应均可通过挂载子组件来设置。**
>
> **内部所写的非UI逻辑需要封装在一个或多个函数内。**

### 开发步骤

1. **界面开发,ArkTS侧语法介绍**

   ```
   XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' })
     .onLoad((context) => {})
     .onDestroy(() => {})
   ```

   * **id : 与XComponent组件为一一对应关系,不可重复。通常开发者可以在native侧通过OH\_NativeXComponent\_GetXComponentId接口来获取对应的id从而绑定对应的XComponent。**
   * **libraryname:加载模块的名称,必须与在native侧Napi模块注册时nm\_modname的名字一致。**

     > **说明:**
     >
     > **应用加载模块实现跨语言调用有两种方式:**
     >
     > 1. **使用NAPI的import方式加载:**
     >    ```
     >    import nativerender from "libnativerender.so"
     >    ```
     > 2. **使用XComponent组件加载,本质也是使用了NAPI机制来加载。 该加载方式和import加载方式的区别在于,在加载动态库时会将XComponent的NativeXComponent实例暴露到应用的native层中,从而让开发者可以使用XComponent的NDK接口。**
     >
   * **onLoad事件**

     * **触发时刻:XComponent准备好surface后触发。**
     * **参数context:其上面挂载了暴露在模块上的native方法,使用方法类似于利用 import context2 from "libnativerender.so" 直接加载模块后获得的context2实例。**
     * **时序:XComponent组件Surface可用;native层OnSurfaceCreated触发;OnSurfaceCreate回调执行完成;js层onLoad触发;**
   * **onDestroy事件**
     **触发时刻:XComponent组件被销毁时触发与一般ArkUI的组件销毁时机一致。**
     **时序:natvice层OnSurfaceDestroyed触发;OnSurfaceDestroyed回调执行完成;js层onDestroy触发;XComponent组件Surface销毁;**

   ---

   **xcomponent两种用法:**


   1. **通过绑定XComponentController获得对应XComponent的surfaceId(该id可以唯一确定一个surface),从而传给相应的部件(如 **[AVPlayer](https://gitee.com/openharmony/do ... -media.md#avplayer9)、[Camera](https://gitee.com/openharmony/do ... t/js-apis-camera.md)  )接口进行渲染。如:
      ```
      class suf{
        surfaceId:string = "";
        mXComponentController: XComponentController = new XComponentController();
        set(){
          this.surfaceId = this.mXComponentController.getXComponentSurfaceId()
        }
      }
      @State surfaceId:string = "";
      mXComponentController: object = new XComponentController();
      XComponent({ id: '', type: 'surface', controller: this.mXComponentController })
        .onLoad(() => {
          let sufset = new suf()
          sufset.set()
        })
      ```
   2. **通过初始化时传入native lib,然后调用ndk方法进行渲染,本章主讲这条路。**
      ```
      // JS侧的接口声明
      export default interface XComponentContext {
        drawPattern(): void;

        getStatus(): XComponentContextStatus;
      };

      type XComponentContextStatus = {
        hasDraw: boolean,
        hasChangeColor: boolean,
      };

      // ets 里的组件构建和使用
      Column({ space: 10 }) {
        XComponent({
          id: 'xcomponentId',
          type: XComponentType.SURFACE,
          libraryname: 'nativerender'
        })
          .onLoad((xComponentContext) => {
            this.xComponentContext = xComponentContext as XComponentContext;
            this.currentStatus = "index";
          })
          .onDestroy(() => {
            console.log('onDestroy');
          })

        Text(this.currentStatus)
          .fontSize('24fp')
          .fontWeight(500)
      }
      .onClick(() => {
        let hasChangeColor: boolean = false;
        if (this.xComponentContext && this.xComponentContext.getStatus()) {
          hasChangeColor = this.xComponentContext.getStatus().hasChangeColor;
        }
        if (hasChangeColor) {
          this.currentStatus = "change color";
        }
      })

      Row() {
        Button('Draw BMP')
          .onClick(() => {
            if (this.xComponentContext) {
              this.xComponentContext.drawPattern();
              let hasDraw: boolean = false;
              if (this.xComponentContext.getStatus()) {
                hasDraw = this.xComponentContext.getStatus().hasDraw;
              }
              if (hasDraw) {
                this.currentStatus = "draw bmp";
              }
            }
          })
      }
      ```

   ---
2. **Native渲染**

   1. **CMakeList**

      ```
      # 编译目标so,SHARED表示动态库
      add_library(nativerender SHARED
                  xxx.cpp
                  )

      # 查找相关库 (包括OpenGL ES相关库和XComponent提供的ndk接口)
      find_library( EGL-lib
                    EGL )

      find_library( GLES-lib
                    GLESv3 )

      find_library( libace-lib
                    ace_ndk.z )

      # 编译so所需要的依赖
      target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${libace-lib} libace_napi.z.so libc++.a)
      ```
   2. **Napi模块注册**

      ```
      // 在so被xcomponent在js层加载的时候会调用init方法进行初始化,本质就是传递nativeXComponent对象进CPP层
      static napi_value Init(napi_env env, napi_value exports)
      {
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins");
          if ((env == nullptr) || (exports == nullptr)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null");
              return nullptr;
          }

          napi_property_descriptor desc[] = {
              { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }
          };
          if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
              return nullptr;
          }

          PluginManager::GetInstance()->Export(env, exports);
          return exports;
      }
      ```
   3. **获得NativeXComponent实例**

      ```
      // Init方法最后调用实例化方法
      void PluginManager::Export(napi_env env, napi_value exports)
      {
          if ((env == nullptr) || (exports == nullptr)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginManager", "Export: env or exports is null");
              return;
          }
          // 从js对象里取OH_NATIVE_XCOMPONENT_OBJ对象
          napi_value exportInstance = nullptr;
          if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginManager", "Export: napi_get_named_property fail");
              return;
          }
          // 解封装拿到NDK指针
          OH_NativeXComponent* nativeXComponent = nullptr;
          if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginManager", "Export: napi_unwrap fail");
              return;
          }
          // 通过NDK 接口获取对应 JS层的 ID,此ID其实就是在JS层初始化时的 XComponent 的属性 id = "xcomponentId";
          char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
          uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
          if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginManager", "Export: OH_NativeXComponent_GetXComponentId fail");
              return;
          }
          // 通过XComponent的ID来绑定JS层的组件和Native层的render;此处context和render都是native层的全局变量,通过id绑定和回调注册等待组件初始化完成并获得想要的window指针
          std::string id(idStr);
          auto context = PluginManager::GetInstance();
          if ((context != nullptr) && (nativeXComponent != nullptr)) {
              context->SetNativeXComponent(id, nativeXComponent);
              auto render = context->GetRender(id);
              if (render != nullptr) {
                  // 注册回调,收听NDK的回调消息
                  render->RegisterCallback(nativeXComponent);
                  // 注册render的对外暴露方法,JS层可以访问
                  render->Export(env, exports);
              }
          }
      }
      ```
   4. **注册XComponent回调,具体内容看 **[native xcomponent](https://docs.openharmony.cn/page ... tive_x_component.md)

      ```
      // 此处需要根据NDK文档进行自学
      void PluginRender::RegisterCallback(OH_NativeXComponent* nativeXComponent)
      {
          renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
          renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
          renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
          renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
          OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_);

          mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
          mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
          OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &mouseCallback_);
          OH_NativeXComponent_RegisterFocusEventCallback(nativeXComponent, OnFocusEventCB);
          OH_NativeXComponent_RegisterKeyEventCallback(nativeXComponent, OnKeyEventCB);
          OH_NativeXComponent_RegisterBlurEventCallback(nativeXComponent, OnBlurEventCB);
      }
      ```
   5. **创建EGL/OpenGLES**

      ```
      // so 加载的时候初始化 EGL对象
      PluginRender:luginRender(std::string& id)
      {
          this->id_ = id;
          this->eglCore_ = new EGLCore();
      }

      // xcomponent加载完毕,创建 surface 开始初始化EGL
      void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
      {
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB");
          if ((component == nullptr) || (window == nullptr)) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: component or window is null");
              return;
          }

          char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
          uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
          if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceCreatedCB: Unable to get XComponent id");
              return;
          }

          std::string id(idStr);
          auto render = PluginRender::GetInstance(id);
          uint64_t width;
          uint64_t height;
          int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
          if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
              if (render->eglCore_->EglContextInit(window, width, height)) {
                  render->eglCore_->Background();
              }
          }
      }

      // EGL 初始化窗口
      bool EGLCore::EglContextInit(void* window, int width, int height)
      {
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute");
          if ((window == nullptr) || (width <= 0) || (height <= 0)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit: param error");
              return false;
          }

          UpdateSize(width, height);
          eglWindow_ = static_cast<EGLNativeWindowType>(window);

          // Init display.
          eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
          if (eglDisplay_ == EGL_NO_DISPLAY) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
              return false;
          }

          EGLint majorVersion;
          EGLint minorVersion;
          if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display");
              return false;
          }

          // Select configuration.
          const EGLint maxConfigSize = 1;
          EGLint numConfigs;
          if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
              return false;
          }

          return CreateEnvironment();
      }

      // 创建 EGL 环境创建,eglsurface 和 eglcontext 是渲染的界面和渲染上下文
      bool EGLCore::CreateEnvironment()
      {
          // Create surface.
          if (eglWindow_ == nullptr) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null");
              return false;
          }
          eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
          if (eglSurface_ == nullptr) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
              eglTerminate(eglDisplay_);
              return false;
          }
          // Create context.
          eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
          if (eglContext_ == nullptr) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
              eglDestroySurface(eglDisplay_, eglSurface_);
              eglTerminate(eglDisplay_);
              return false;
          }

          if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
              eglDestroyContext(eglDisplay_, eglContext_);
              eglDestroySurface(eglDisplay_, eglSurface_);
              eglTerminate(eglDisplay_);
              return false;
          }
          // Create program,创建纹理和对应的着色器程序.
          program_ = TRCreateProgram(EGLTR_VERTEX_SHADER, EGLTR_FRAGMENT_SHADER);
          //    program_ = CreateProgram(EGLDRAW_VERTEX_SHADER, EGLDRAW_FRAGMENT_SHADER);
          if (program_ == PROGRAM_ERROR) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
              return false;
          }
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: success!");
          return true;
      }
      ```
   6. **进行绘制**

      ```
      // js层
      this.xComponentContext.drawPattern();

      // 接口注册
      {"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},

      // 接口实现
      napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info)
      {
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern");
          if ((env == nullptr) || (info == nullptr)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern: env or info is null");
              return nullptr;
          }
          // 取传参
          napi_value thisArg;
          if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern: napi_get_cb_info fail");
              return nullptr;
          }
          // 从传参里取js层xcomponent对象
          napi_value exportInstance;
          if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern: napi_get_named_property fail");
              return nullptr;
          }
          // 解封装获得native层的xcomponent对象
          OH_NativeXComponent* nativeXComponent = nullptr;
          if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern: napi_unwrap fail");
              return nullptr;
          }
          // 获取xcomponent的id
          char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
          uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
          if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
              OH_LOG_Print(
                  LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "luginRender", "NapiDrawPattern: Unable to get XComponent id");
              return nullptr;
          }
          // 根据id找render,然后就可以拿eglcore画图
          std::string id(idStr);
          PluginRender* render = PluginRender::GetInstance(id);
          if (render != nullptr) {
              render->eglCore_->DrawBmp(hasDraw_);
              OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed");
          }
          return nullptr;
      }
      ```
   7. **EGL 方法**

      ```
      void EGLCore:rawBmp(uint32_t fd, uint32_t foff, uint32_t flen)
      {
          flag_ = false;
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Drawbmp");

          FILE *file = fdopen(fd, "r");
          if (file == NULL) {
              OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ResfdExecuteCB fdopen failed!");
              return;
          }
          
          if (fseek(file, foff, SEEK_SET) != 0) {
              OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ResfdExecuteCB fseek failed!");
              return;
          }
          
          unsigned char *mediaData = new unsigned char[flen];
          if (mediaData == NULL) {
              OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "new buffer failed!");
              fclose(file);
              return;
          }
          size_t readCnt = fread(mediaData, sizeof(unsigned char), flen, file);
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "readcnt: %{public}zu", readCnt);

          int width = 0;
          int height = 0;
          unsigned char *pdata = mediaData + NUM_18;
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "width: %{public}d", *((uint32_t *)pdata));
          width = *((uint32_t *)pdata);
          pdata = mediaData + NUM_18 + NUM_4;
          OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "height: %{public}d", *((uint32_t *)pdata));
          height = *((uint32_t *)pdata);

          float vertices[] = {
              // positions          // colors           // texture coords
               1.0f,  1.0f, 0.0f,   1.0f, 0.0f, 0.0f,   2.0f, 2.0f, // top right
               1.0f,  -1.0f, 0.0f,   0.0f, 1.0f, 0.0f,   2.0f, -1.0f, // bottom right
               -1.0f,  -1.0f, 0.0f,   0.0f, 0.0f, 1.0f,   -1.0f, -1.0f, // bottom left
               -1.0f,  1.0f, 0.0f,   1.0f, 1.0f, 0.0f,   -1.0f, 2.0f  // top left
          };

          unsigned int indices[] = {  
              0, 1, 3, // first triangle
              1, 2, 3  // second triangle
          };
          unsigned int VBO = 0;
          unsigned int VAO = 0;
          unsigned int EBO = 0;
          glGenVertexArrays(1, &VAO);
          glGenBuffers(1, &VBO);
          glGenBuffers(1, &EBO);

          glBindVertexArray(VAO);

          glBindBuffer(GL_ARRAY_BUFFER, VBO);
          glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

          glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
          glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

          // position attribute
          glVertexAttribPointer(0, NUM_3, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)0);
          glEnableVertexAttribArray(0);
          // color attribute
          glVertexAttribPointer(1, NUM_3, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)(NUM_3 * sizeof(float)));
          glEnableVertexAttribArray(1);
          // texture coord attribute
          glVertexAttribPointer(NUM_2, NUM_2, GL_FLOAT, GL_FALSE, NUM_8 * sizeof(float), (void*)(NUM_6 * sizeof(float)));
          glEnableVertexAttribArray(NUM_2);


          // load and create a texture
          // -------------------------
          unsigned int texture;
          glGenTextures(1, &texture);
          glBindTexture(GL_TEXTURE_2D, texture);
          // all upcoming GL_TEXTURE_2D operations now have effect on this texture object
          // set the texture wrapping parameters
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   
          // set texture wrapping to GL_CLAMP_TO_EDGE (default wrapping method)
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
          // set texture filtering parameters
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
          glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
          // load image, create texture and generate mipmaps
          glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, mediaData);
          glGenerateMipmap(GL_TEXTURE_2D);

          glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
          glClear(GL_COLOR_BUFFER_BIT);

          glBindTexture(GL_TEXTURE_2D, texture);

          glUseProgram(program_);

          glBindVertexArray(VAO);
          glDrawElements(GL_TRIANGLES, NUM_6, GL_UNSIGNED_INT, 0);

          eglSwapBuffers(eglDisplay_, eglSurface_);

          hasDraw = 1;
          flag_ = true;
      }
      ```
   8. **结果展示**

      ![image.png](https://forums-obs.openharmony.c ... yyyhyn50h2by5c6.png "image.png")

### 总结

**可以利用OpenHarmony的hsp和har,包装组件和对应的NDK工程,提供给其他应用进行开发和应用。**
[/md]




欢迎光临 OpenHarmony开发者论坛 (https://forums.openharmony.cn/) Powered by Discuz! X3.5