From b945da4b327f1b330465fe4c6366fb7e3dbc67ed Mon Sep 17 00:00:00 2001 From: WangJiazhen Date: Fri, 30 Jun 2023 15:22:11 +0800 Subject: [PATCH] Add Native XComponent guidelines Signed-off-by: WangJiazhen Change-Id: Ia80431830812d115962c242619820f067a0feb43 --- zh-cn/application-dev/napi/Readme-CN.md | 1 + .../napi/figures/onDestroy.png | Bin 0 -> 27198 bytes zh-cn/application-dev/napi/figures/onLoad.png | Bin 0 -> 25709 bytes .../napi/xcomponent-guidelines.md | 814 ++++++++++++++++++ 4 files changed, 815 insertions(+) create mode 100644 zh-cn/application-dev/napi/figures/onDestroy.png create mode 100644 zh-cn/application-dev/napi/figures/onLoad.png create mode 100644 zh-cn/application-dev/napi/xcomponent-guidelines.md diff --git a/zh-cn/application-dev/napi/Readme-CN.md b/zh-cn/application-dev/napi/Readme-CN.md index d465686b9b..c636ceb7e2 100644 --- a/zh-cn/application-dev/napi/Readme-CN.md +++ b/zh-cn/application-dev/napi/Readme-CN.md @@ -8,3 +8,4 @@ - [使用MindSpore Lite进行离线模型的转换及推理](mindspore-lite-offline-model-guidelines.md) - [Neural Network Runtime对接AI推理框架开发指导](neural-network-runtime-guidelines.md) - [Purgeable memory开发指导](purgeable-memory-guidelines.md) +- [XComponent开发指导](xcomponent-guidelines.md) \ No newline at end of file diff --git a/zh-cn/application-dev/napi/figures/onDestroy.png b/zh-cn/application-dev/napi/figures/onDestroy.png new file mode 100644 index 0000000000000000000000000000000000000000..ff32cdae3e35bd9b05e0f676fd3774fd357036ff GIT binary patch literal 27198 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 literal 0 HcmV?d00001 diff --git a/zh-cn/application-dev/napi/xcomponent-guidelines.md b/zh-cn/application-dev/napi/xcomponent-guidelines.md new file mode 100644 index 0000000000..da4e813922 --- /dev/null +++ b/zh-cn/application-dev/napi/xcomponent-guidelines.md @@ -0,0 +1,814 @@ +# XComponent开发指导 + +## 场景介绍 + +Native XComponent是XComponent组件提供在Native层的实例,可作为JS层和Native层XComponent绑定的桥梁。XComponent所提供的的NDK接口都依赖于该实例。接口能力包括获取Native Window实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下: + +- 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。 +- 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。 +- 利用Native Window和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。 + +## 接口说明 + +| 接口名 | 描述 | +| -------- | -------- | +|OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size)|获取XComponent的id。| +|OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height)|获取XComponent持有的surface的大小。| +|OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y)|获取XComponent持有的surface相对窗口左上角的偏移量。| +|OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent)|获取由XComponent触发的触摸事件。| +|OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType)|获取XComponent触摸点的工具类型。| +|OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX)|获取XComponent触摸点处相对X轴的倾斜角度。| +|OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY)|获取XComponent触摸点处相对Y轴的倾斜角度。| +|OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent)|获取由XComponent触发的鼠标事件。| +|OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback)|为此OH_NativeXComponent实例注册生命周期和触摸事件回调。| +|OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback)|为此OH_NativeXComponent实例注册鼠标事件回调。| +|OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|为此OH_NativeXComponent实例注册获得焦点事件回调。| +|OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|为此OH_NativeXComponent实例注册按键事件回调。| +|OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window))|为此OH_NativeXComponent实例注册失去焦点事件回调。| +|OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent)|获取由XComponent触发的按键事件。| +|OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action)|获取按键事件的动作。| +|OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code)|获取按键事件的键码值。| +|OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType)|获取按键事件的输入源类型。| +|OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId)|获取按键事件的设备ID。| +|OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp)|获取按键事件的时间戳。| + +## 生命周期说明 + +开发者在ArkTS侧使用如下代码即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。 + +```typescript +XComponent({ id: 'xcomponentId1', type: 'surface', libraryname: 'nativerender' }) + .onLoad((context) => {}) + .onDestroy(() => {}) +``` + +### onLoad事件 + +触发时刻:XComponent准备好surface后触发。 + +参数context:其上面挂载了暴露在模块上的Native方法,使用方法类似于利用 import context from "libnativerender.so" 直接加载模块后获得的context实例。 + +时序:onLoad事件的触发和surface相关,其和Native侧的OnSurfaceCreated的时序如下图: + +![onLoad](./figures/onLoad.png) + +### onDestroy事件 + +触发时刻:XComponent组件被销毁时触发与一般ArkUI的组件销毁时机一致,其和Native侧的OnSurfaceDestroyed的时序如下图: + +![onDestroy](./figures/onDestroy.png) + +## 开发步骤 +以下步骤描述了在**OpenHarmony**中如何使用`XComponent组件`调用`NAPI`接口来创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。 + +1. **在界面中定义XComponent**。 + + ```typescript + // ... + // 在xxx.ets 中定义 XComponent + XComponent({ + id: 'xcomponentId', + type: XComponentType.SURFACE, + libraryname: 'nativerender' + }) + .focusable(true) // 可响应键盘事件 + .onLoad((xComponentContext) => { + this.xComponentContext = xComponentContext; + }) + .onDestroy(() => { + console.log("onDestroy"); + }) + // ... + ``` + +2. **Napi模块注册**,具体使用请参考[Native API在应用工程中的使用指导](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/napi/napi-guidelines.md)。 + + ```c++ + // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供JS侧调用 + EXTERN_C_START + static napi_value Init(napi_env env, napi_value exports) + { + // ... + // 向JS侧暴露接口getContext() + 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; + } + // 方法内检查环境变量是否包含XComponent组件实例,若实例存在注册绘制相关接口 + PluginManager::GetInstance()->Export(env, exports); + return exports; + } + EXTERN_C_END + + // 编写接口的描述信息,根据实际需要可以修改对应参数 + static napi_module nativerenderModule = { + .nm_version = 1, + .nflag_s = 0, + .nm_filename = nullptr, + // 入口函数 + .nm_register_func = Init, + // 模块名称 + .nm_modname = "nativerender", + .nm_priv = ((void *)0), + .reserved = { 0 } + }; + + // __attribute__((constructor))修饰的方法由系统自动调用,使用NAPI接口napi_module_register()传入模块描述信息进行模块注册 + extern "C" __attribute__((constructor)) void RegisterModule(void) + { + napi_module_register(&nativerenderModule); + } + + // 使用NAPI中的napi_define_properties方法,向JS侧暴露drawPattern()方法,在JS侧调用drawPattern()来绘制内容。 + void PluginRender::Export(napi_env env, napi_value exports) + { + // ... + // 将接口函数注册为JS侧接口drawPattern + napi_property_descriptor desc[] = { + { "drawPattern", nullptr, PluginRender::NapiDrawPattern, 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, "PluginRender", "Export: napi_define_properties failed"); + } + } + ``` + +3. **注册XComponent事件回调**,使用NAPI实现XComponent事件回调函数。 + + (1) 定义surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。 + + ```c++ + // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景 + void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) + { + // ... + // 获取XComponent的id,即JS侧XComponent组件构造中的id参数 + 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; + // 获取XComponent拥有的surface的大小 + 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(); + } + } + } + + // 定义一个函数OnSurfaceChangedCB() + void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) + { + // ... + // 获取XComponent的id + 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", + "OnSurfaceChangedCB: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render != nullptr) { + // 封装OnSurfaceChanged方法 + render->OnSurfaceChanged(component, window); + } + } + + // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中 + void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) + { + // ... + // 获取XComponent的id + 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", + "OnSurfaceDestroyedCB: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + // 释放资源 + PluginRender::Release(id); + } + + // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调 + void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) + { + // ... + // 获取XComponent的id + 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", + "DispatchTouchEventCB: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + PluginRender *render = PluginRender::GetInstance(id); + if (render != nullptr) { + // 封装OnTouchEvent方法 + render->OnTouchEvent(component, window); + } + } + + // 定义一个函数DispatchMouseEventCB(),响应鼠标事件时触发该回调 + void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchMouseEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render) { + // 封装OnMouseEvent方法 + render->OnMouseEvent(component, window); + } + } + + // 定义一个函数DispatchHoverEventCB(),响应鼠标悬停事件时触发该回调 + void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchHoverEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render) { + // 封装OnHoverEvent方法 + render->OnHoverEvent(component, isHover); + } + } + + // 定义一个函数OnFocusEventCB(),响应获焦事件时触发该回调 + void OnFocusEventCB(OH_NativeXComponent *component, void *window) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnFocusEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render) { + // 封装OnFocusEvent方法 + render->OnFocusEvent(component, window); + } + } + + // 定义一个函数OnBlurEventCB(),响应失去焦点事件时触发该回调 + void OnBlurEventCB(OH_NativeXComponent *component, void *window) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnBlurEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render) { + // 封装OnBlurEvent方法 + render->OnBlurEvent(component, window); + } + } + + // 定义一个函数OnKeyEventCB(),响应按键事件时触发该回调 + void OnKeyEventCB(OH_NativeXComponent *component, void *window) { + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnKeyEventCB"); + int32_t ret; + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); + if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + return; + } + std::string id(idStr); + auto render = PluginRender::GetInstance(id); + if (render) { + // 封装OnKeyEvent方法 + render->OnKeyEvent(component, window); + } + } + ``` + + (2) 注册XComponent事件回调函数,在XComponent事件触发时调用3.1步骤中定义的方法。 + + ```c++ + void PluginRender::RegisterCallback(OH_NativeXComponent *nativeXComponent) { + // 设置组件创建事件的回调函数,组件创建时触发相关操作,初始化环境与绘制背景 + renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; + // 设置组件改变事件的回调函数,组件改变时触发相关操作 + renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB; + // 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源 + renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; + // 设置触摸事件的回调函数,在触摸事件触发时调用NAPI接口函数,从而调用原C++方法 + renderCallback_.DispatchTouchEvent = DispatchTouchEventCB; + // 将OH_NativeXComponent_Callback注册给NativeXComponent + OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_); + + // 设置鼠标事件的回调函数,在触摸事件触发时调用NAPI接口函数,从而调用原C++方法 + mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; + // 设置鼠标悬停事件的回调函数,在触摸事件触发时调用NAPI接口函数,从而调用原C++方法 + mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; + // 将OH_NativeXComponent_MouseEvent_Callback注册给NativeXComponent + OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &mouseCallback_); + + // 将OnFocusEventCB方法注册给NativeXComponent + OH_NativeXComponent_RegisterFocusEventCallback(nativeXComponent, OnFocusEventCB); + // 将OnKeyEventCB方法注册给NativeXComponent + OH_NativeXComponent_RegisterKeyEventCallback(nativeXComponent, OnKeyEventCB); + // 将OnBlurEventCB方法注册给 NativeXComponent + OH_NativeXComponent_RegisterBlurEventCallback(nativeXComponent, OnBlurEventCB); + } + ``` + + (3) 定义NapiDrawPattern方法,暴露到JS侧的drawPattern()方法会执行该方法。 + + ```c++ + napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) + { + // ... + // 获取环境变量参数 + 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; + } + + // 获取环境变量中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; + } + + // 通过napi_unwrap接口,获取XComponent的实例指针 + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&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; + } + + std::string id(idStr); + PluginRender *render = PluginRender::GetInstance(id); + if (render) { + // 调用绘制方法 + render->eglCore_->Draw(); + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed"); + } + return nullptr; + } + ``` + +4. **初始化环境**,包括初始化可用的EGLDisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。 + + ```c++ + void EGLCore::UpdateSize(int width, int height) + { + width_ = width; + height_ = height; + if (width_ > 0) { + // 计算绘制矩形宽度百分比 + width_Percent_ = FIFTY_PERCENT * height_ / width_; + } + } + + bool EGLCore::EglContextInit(void *window, int width, int height) + { + // ... + UpdateSize(width, height); + eglWindow_ = static_cast(window); + + // 初始化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; + } + + // 初始化EGL + 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; + } + + // 选择配置 + 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(); + } + ``` + + ```c++ + bool EGLCore::CreateEnvironment() + { + // ... + // 创建surface + eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); + + // ... + // 创建context + eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); + if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); + return false; + } + + // 创建program + program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); + if (program_ == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); + return false; + } + return true; + } + ``` + +5. **渲染功能实现**。 + + (1) 绘制背景。 + + ```c++ + // 绘制背景颜色 #f4f4f4 + const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f }; + + // 绘制背景顶点 + const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { + -1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + -1.0f, -1.0f + }; + ``` + + ```c++ + // 绘制背景颜色 + void EGLCore::Background() + { + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, + sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); + return; + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); + return; + } + } + + // 绘前准备,获取position,创建成功时position值从0开始 + GLint EGLCore::PrepareDraw() + { + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || + (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); + return POSITION_ERROR; + } + + glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); + glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(program_); + + return glGetAttribLocation(program_, POSITION_NAME); + } + + // 依据传入参数在指定区域绘制指定颜色 + bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], + unsigned long vertSize) + { + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glEnableVertexAttribArray(position); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + + return true; + } + + // 结束绘制操作 + bool EGLCore::FinishDraw() + { + // 强制刷新缓冲 + glFlush(); + glFinish(); + return eglSwapBuffers(eglDisplay_, eglSurface_); + } + ``` + + (2) 绘制图形。 + + ```c++ + void EGLCore::Draw() + { + flag_ = false; + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); + return; + } + + // 绘制背景 + if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, + sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); + return; + } + + // 将五角星分为五个四边形,计算其中一个四边形的四个顶点 + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + // 确定绘制四边形的顶点,使用绘制区域的百分比表示 + const GLfloat shapeVertices[] = { + centerX / width_, centerY / height_, + leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, + rightX / width_, rightY / height_ + }; + + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); + return; + } + + GLfloat rad = M_PI / 180 * 72; + for (int i = 0; i < 4; ++i) + { + // 旋转得其他四个四边形的顶点 + rotate2d(centerX, centerY, &rotateX, &rotateY,rad); + rotate2d(centerX, centerY, &leftX, &leftY,rad); + rotate2d(centerX, centerY, &rightX, &rightY,rad); + + // 确定绘制四边形的顶点,使用绘制区域的百分比表示 + const GLfloat shapeVertices[] = { + centerX / width_, centerY / height_, + leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, + rightX / width_, rightY / height_ + }; + + // 绘制图形 + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); + return; + } + } + + // 结束绘制 + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); + return; + } + + flag_ = true; + } + ``` + + (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。 + + ```c++ + void EGLCore::ChangeColor() + { + if (!flag_) { + return; + } + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); + return; + } + + // 绘制背景 + if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, + sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); + return; + } + + // 确定绘制四边形的顶点,使用绘制区域的百分比表示 + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + // 确定绘制四边形的顶点,使用绘制区域的百分比表示 + const GLfloat shapeVertices[] = { + centerX / width_, centerY / height_, + leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, + rightX / width_, rightY / height_ + }; + + // 使用新的颜色绘制 + if (!ExecuteDrawStar2(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); + return; + } + + GLfloat rad = M_PI / 180 * 72; + for (int i = 0; i < 4; ++i) + { + // 旋转得其他四个四边形的顶点 + rotate2d(centerX, centerY, &rotateX, &rotateY,rad); + rotate2d(centerX, centerY, &leftX, &leftY,rad); + rotate2d(centerX, centerY, &rightX, &rightY,rad); + + // 确定绘制四边形的顶点,使用绘制区域的百分比表示 + const GLfloat shapeVertices[] = { + centerX / width_, centerY / height_, + leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, + rightX / width_, rightY / height_ + }; + + // 使用新的颜色绘制 + if (!ExecuteDrawStar2(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); + return; + } + } + + // 结束绘制 + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); + } + } + ``` + +6. **释放相关资源**。 + + (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。 + + ```c++ + void EGLCore::Release() + { + // 释放surface + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); + } + // 释放context + if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); + } + // 释放display + if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); + } + } + ``` + + (2) PluginRender类添加Release()方法,释放EGLCore实例及PluginRender实例。 + + ```c++ + void PluginRender::Release(std::string &id) + { + PluginRender *render = PluginRender::GetInstance(id); + if (render != nullptr) { + render->eglCore_->Release(); + delete render->eglCore_; + render->eglCore_ = nullptr; + delete render; + render = nullptr; + instance_.erase(instance_.find(id)); + } + } + ``` + +7. **CMakeLists**,使用CMake工具链将C++源代码编译成动态链接库文件。 + + ```CMake + # 设置CMake最小版本 + cmake_minimum_required(VERSION 3.4.1) + # 项目名称 + project(XComponent) + + set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + add_definitions(-DOHOS_PLATFORM) + # 设置头文件搜索目录 + include_directories( + ${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include + ) + # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件 + add_library(nativerender SHARED + render/egl_core.cpp + render/plugin_render.cpp + manager/plugin_manager.cpp + napi_init.cpp + ) + + find_library( + EGL-lib + EGL + ) + + find_library( + GLES-lib + GLESv3 + ) + + find_library( + hilog-lib + hilog_ndk.z + ) + + find_library( + libace-lib + ace_ndk.z + ) + + find_library( + libnapi-lib + ace_napi.z + ) + + find_library( + libuv-lib + uv + ) + # 添加构建需要链接的库 + target_link_libraries(nativerender PUBLIC + ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib}) + ``` + +## 相关实例 + +针对Native XComponent的使用,有以下相关实例可供参考: + +- [使用Native XComponent接口绘制图形](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkXComponent) -- GitLab