[经验分享] OpenHarmony应用开发引入开源C/C++库---之EGL Render 原创 精华

深开鸿_王石 显示全部楼层 发表于 2024-4-16 18:00:32

前言

napi_generator: NAPI框架生成工具 - Gitee.com

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 给组件绑定一个控制器,通过控制器调用组件方法,仅XComponent类型为"surface"时有效。
XComponent10+

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

参数:

参数名 参数类型 必填 描述
id string 组件的唯一标识,支持最大的字符串长度128。
type XComponentType 用于指定XComponent组件类型。
libraryname string 用Native层编译输出动态库名称,仅类型为SURFACE或TEXTURE时有效。
controller XComponentcontroller 给组件绑定一个控制器,通过控制器调用组件方法,仅类型为SURFACE或TEXTURE时有效。
XComponentType10+枚举说明
名称 描述
SURFACE 用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容单独展示到屏幕上。
COMPONENT XComponent将变成一个容器组件,并可在其中执行非UI逻辑以动态加载显示内容。
TEXTURE 用于EGL/OpenGLES和媒体数据写入,开发者定制的绘制内容会和XComponent组件的内容合成后展示到屏幕上。

说明:

type为COMPONENT("component")时,XComponent作为容器,子组件沿垂直方向布局:

  • 垂直方向上对齐格式:FlexAlign.Start
  • 水平方向上对齐格式: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),从而传给相应的部件(如 AVPlayerCamera )接口进行渲染。如:

      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, "PluginManager", "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, "PluginManager", "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, "PluginManager", "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, "PluginManager", "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

      // 此处需要根据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::PluginRender(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, "PluginRender", "NapiDrawPattern");
          if ((env == nullptr) || (info == nullptr)) {
              OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "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, "PluginRender", "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, "PluginRender", "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, "PluginRender", "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, "PluginRender", "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::DrawBmp(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

总结

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

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部