• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 自定义渲染 (XComponent)
2
3## 概述
4
5XComponent组件作为一种渲染组件,可用于EGL/OpenGLES和媒体数据写入,通过使用XComponent持有的“[NativeWindow](../graphics/native-window-guidelines.md)”来渲染画面,通常用于满足开发者较为复杂的自定义渲染需求,例如相机预览流的显示和游戏画面的渲染。其可通过指定type字段来实现不同的渲染方式,分别为[XComponentType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#xcomponenttype10).SURFACE和XComponentType.TEXTURE。对于SURFACE类型,开发者将定制的绘制内容单独展示到屏幕上。对于TEXTURE类型,开发者将定制的绘制内容和XComponent组件的内容合成后展示到屏幕上。
6
7目前XComponent组件主要有两个应用场景。一个是Native XComponent场景,在native层获取Native XComponent实例,在native侧注册XComponent的生命周期回调,以及触摸、鼠标、按键等事件回调。另一个是ArkTS XComponent场景,在ArkTS侧获取SurfaceId,生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发。
8
9## Native XComponent场景
10在XComponent组件构造函数的libraryname中定义需要加载的动态库,而后应用就可以在Native层获取Native XComponent实例,其是XComponent组件提供在Native层的实例,可作为ArkTS层和Native层XComponent绑定的桥梁。XComponent所提供的NDK接口都依赖于该实例。接口能力包括获取NativeWindow实例、获取XComponent的布局/事件信息、注册XComponent的生命周期回调、注册XComponent的触摸、鼠标、按键等事件回调。针对Native XComponent,主要的开发场景如下:
11
12- 利用Native XComponent提供的接口注册XComponent的生命周期和事件回调。
13- 在这些回调中进行初始化环境、获取当前状态、响应各类事件的开发。
14- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
15
16**约束条件**:
17
181、构造XComponent时需要配置libraryname参数、id参数以及type参数。
19
202、id需要唯一。
21
22> **说明**:
23>
24> 1. Native侧的OH_NativeXComponent缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。
25>
26> 2. 对于使用[typeNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#typenode12)创建的SURFACE或TEXTURE类型的XComponent组件,由于typeNode组件的生命周期与声明式组件存在差异,组件在创建后的缓冲区尺寸为未设置状态,因此在开始绘制内容之前,应调用[OH_NativeWindow_NativeWindowHandleOpt](../reference/apis-arkgraphics2d/_native_window.md#oh_nativewindow_nativewindowhandleopt)接口进行缓冲区尺寸设置。
27>
28> 3. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。
29
30**接口说明**
31
32| 接口名                                                       | 描述                                                         |
33| ------------------------------------------------------------ | ------------------------------------------------------------ |
34| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | 获取XComponent的id。                                         |
35| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | 获取XComponent持有的surface的大小。                          |
36| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | 获取XComponent持有的surface相对其父组件左顶点的偏移量。      |
37| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | 获取由XComponent触发的触摸事件。touchEvent内的具体属性值可参考[OH_NativeXComponent_TouchEvent](../reference/apis-arkui/_o_h___native_x_component___touch_event.md)。 |
38| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | 获取XComponent触摸点的工具类型。                             |
39| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | 获取XComponent触摸点处相对X轴的倾斜角度。                    |
40| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | 获取XComponent触摸点处相对Y轴的倾斜角度。                    |
41| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | 获取由XComponent触发的鼠标事件。                             |
42| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | 为此OH_NativeXComponent实例注册生命周期和触摸事件回调。      |
43| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | 为此OH_NativeXComponent实例注册鼠标事件回调。                |
44| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册获得焦点事件回调。            |
45| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册按键事件回调。                |
46| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | 为此OH_NativeXComponent实例注册失去焦点事件回调。            |
47| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | 获取由XComponent触发的按键事件。                             |
48| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | 获取按键事件的动作。                                         |
49| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | 获取按键事件的键码值。                                       |
50| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | 获取按键事件的输入源类型。                                   |
51| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | 获取按键事件的设备ID。                                       |
52| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | 获取按键事件的时间戳。                                       |
53
54> **说明 :**
55>
56> 上述接口不支持跨线程访问。
57>
58> XComponent销毁(onSurfaceDestroyed回调触发后)时会释放上述接口中获取的OH_NativeXComponent和window对象。如果再次使用获取的对象,有可能会导致使用野指针或空指针的崩溃问题。
59
60**开发步骤**
61
62以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`调用`Node-API`接口来创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
63
641. 在界面中定义XComponent。
65
66    ```typescript
67    //在ets/interface/XComponentContext.ts中声明native侧接口
68    export default interface XComponentContext {
69      drawPattern(): void;
70
71      getStatus(): XComponentContextStatus;
72    };
73
74    type XComponentContextStatus = {
75      hasDraw: boolean,
76      hasChangeColor: boolean,
77    };
78    ```
79
80    ```typescript
81    import XComponentContext from "../interface/XComponentContext"
82
83    @Entry
84    @Component
85    struct Index {
86        xComponentContext: XComponentContext | undefined = undefined;
87        xComponentAttrs: XComponentAttrs = {
88            id: 'xcomponentId',
89            type: XComponentType.SURFACE,
90            libraryname: 'nativerender'
91        }
92
93        build() {
94        Row() {
95            // ...
96            // 在xxx.ets 中定义 XComponent
97            XComponent(this.xComponentAttrs)
98                .focusable(true) // 可响应键盘事件
99                .onLoad((xComponentContext) => {
100                    console.log("onLoad");
101                    this.xComponentContext = xComponentContext as XComponentContext;
102
103                    // 调用drawPattern绘制内容
104                    if (this.xComponentContext) {
105                        this.xComponentContext.drawPattern();
106                        if (this.xComponentContext.getStatus()) {
107                            this.xComponentContext.getStatus().hasDraw;
108                        }
109                    }
110                })
111                .onDestroy(() => {
112                    console.log("onDestroy");
113                })
114            // ...
115            }
116            .onClick(() => {
117                // 调用getStatus改变绘制内容
118                if (this.xComponentContext && this.xComponentContext.getStatus()) {
119                    this.xComponentContext.getStatus().hasChangeColor;
120                }
121            })
122            .height('100%')
123        }
124    }
125
126    interface XComponentAttrs {
127        id: string;
128        type: number;
129        libraryname: string;
130    }
131    ```
132
1332. Node-API模块注册,具体使用请参考[Native API在应用工程中的使用指导](../napi/napi-guidelines.md)。
134
135    ```c++
136    #include <hilog/log.h>
137    #include "common/common.h"
138    #include "manager/plugin_manager.h"
139
140    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
141    EXTERN_C_START
142    static napi_value Init(napi_env env, napi_value exports) {
143        // ...
144        // 向ArkTS侧暴露接口getContext()
145        napi_property_descriptor desc[] = {
146            { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr }
147        };
148        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
149            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
150            return nullptr;
151        }
152        // 方法内检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
153        PluginManager::GetInstance()->Export(env, exports);
154        return exports;
155    }
156    EXTERN_C_END
157
158    // 编写接口的描述信息,根据实际需要可以修改对应参数
159    static napi_module nativerenderModule = {
160        .nm_version = 1,
161        .nm_flags = 0,
162        .nm_filename = nullptr,
163        // 入口函数
164        .nm_register_func = Init,// 指定加载对应模块时的回调函数
165        // 模块名称
166        .nm_modname =
167            "nativerender", // 指定模块名称,对于XComponent相关开发,这个名称必须和ArkTS侧XComponent中libraryname的值保持一致
168        .nm_priv = ((void *)0),
169        .reserved = {0}};
170
171    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
172    extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); }
173    ```
174
175    ```c++
176    // 检查环境变量是否包含XComponent组件实例,若实例存在则导出绘制相关接口
177    void PluginManager::Export(napi_env env, napi_value exports)
178    {
179        if ((env == nullptr) || (exports == nullptr)) {
180            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: env or exports is null");
181            return;
182        }
183
184        napi_value exportInstance = nullptr;
185        if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
186            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_get_named_property fail");
187        }
188
189        // 获取nativeXComponent
190        OH_NativeXComponent* nativeXComponent = nullptr;
191        if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) {
192            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_unwrap fail");
193            return;
194        }
195
196        // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
197        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
198        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
199        if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
200            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
201                         "Export: OH_NativeXComponent_GetXComponentId fail");
202            return;
203        }
204
205        std::string id(idStr);
206        auto context = PluginManager::GetInstance();
207        if ((context != nullptr) && (nativeXComponent != nullptr)) {
208            context->SetNativeXComponent(id, nativeXComponent);
209            auto render = context->GetRender(id);
210            if (render != nullptr) {
211                // 注册回调函数
212                render->RegisterCallback(nativeXComponent);
213                // 方法内使用Node-API,导出绘制相关接口,向ArkTS侧暴露绘制相关方法
214                render->Export(env, exports);
215            }
216        }
217    }
218    ```
219    ```c++
220    // 使用Node-API中的napi_define_properties方法,向ArkTS侧暴露drawPattern()和getStatus()方法,在ArkTS侧调用drawPattern()来绘制内容,
221    // 调用getStatus()来改变绘制内容
222    void PluginRender::Export(napi_env env, napi_value exports)
223    {
224        // ...
225        // 将接口函数注册为ArkTS侧接口drawPattern和getStatus
226        napi_property_descriptor desc[] = {
227            {"drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
228            {"getStatus", nullptr, PluginRender::TestGetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
229             nullptr}};
230        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
231            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed");
232        }
233    }
234    ```
235
2363. 注册XComponent事件回调,使用Node-API实现XComponent事件回调函数。
237
238    (1) 定义surface创建成功,发生改变,销毁和XComponent的touch事件回调接口。
239
240    ```c++
241    // 在头文件中定义PluginRender类
242    class PluginRender {
243    public:
244        explicit PluginRender(std::string& id);
245        ~PluginRender() {
246            if (eglCore_ != nullptr) {
247                eglCore_->Release();
248                delete eglCore_;
249                eglCore_ = nullptr;
250            }
251        }
252        static PluginRender* GetInstance(std::string& id);
253        static void Release(std::string& id);
254        static napi_value NapiDrawPattern(napi_env env, napi_callback_info info);
255        static napi_value TestGetXComponentStatus(napi_value env, napi_callback_info info);
256        void Export(napi_env env, napi_value exports);
257        void OnSurfaceChanged(OH_NativeXComponent* component, void* window);
258        void OnTouchEvent(OH_NativeXComponent* component, void* window);
259        void OnMouseEvent(OH_NativeXComponent* component, void* window);
260        void OnHoverEvent(OH_NativeXComponent* component, bool isHover);
261        void OnFocusEvent(OH_NativeXComponent* component, void* window);
262        void OnBlurEvent(OH_NativeXComponent* component, void* window);
263        void OnKeyEvent(OH_NativeXComponent* component, void* window);
264        void RegisterCallback(OH_NativeXComponent* NativeXComponent);
265
266    public:
267        static std::unordered_map<std::string, PluginRender*> instance_;
268        EGLCore* eglCore_;
269        std::string id_;
270        static int32_t hasDraw_;
271        static int32_t hasChangeColor_;
272
273    private:
274        OH_NativeXComponent_Callback renderCallback_;
275        OH_NativeXComponent_MouseEvent_Callback mouseCallback_;
276    };
277    ```
278
279    ```c++
280    // 在源文件中实现PluginRender类中方法
281    std::unordered_map<std::string, PluginRender *> PluginRender::instance_;
282    int32_t PluginRender::hasDraw_ = 0;
283    int32_t PluginRender::hasChangeColor_ = 0;
284
285    PluginRender::PluginRender(std::string& id) {
286        this->id_ = id;
287        this->eglCore_ = new EGLCore();
288    }
289
290    PluginRender* PluginRender::GetInstance(std::string& id) {
291        if (instance_.find(id) == instance_.end()) {
292            PluginRender* instance = new PluginRender(id);
293            instance_[id] = instance;
294            return instance;
295        } else {
296            return instance_[id];
297        }
298    }
299
300    // 定义一个函数OnSurfaceCreatedCB(),封装初始化环境与绘制背景
301    void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
302   	    // ...
303   	    // 获取XComponent的id,即ArkTS侧XComponent组件构造中的id参数
304   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
305   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
306   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
307   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
308   			             "OnSurfaceCreatedCB: Unable to get XComponent id");
309   		    return;
310   	    }
311
312   	    // 初始化环境与绘制背景
313   	    std::string id(idStr);
314   	    auto render = PluginRender::GetInstance(id);
315   	    uint64_t width;
316   	    uint64_t height;
317   	    // 获取XComponent拥有的surface的大小
318   	    int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
319   	    if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) {
320   		    if (render->eglCore_->EglContextInit(window, width, height)) {
321   			    render->eglCore_->Background();
322   		    }
323   	    }
324    }
325
326    // 定义一个函数OnSurfaceChangedCB()
327    void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
328   	    // ...
329   	    // 获取XComponent的id
330   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
331   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
332   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
333   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
334   			             "OnSurfaceChangedCB: Unable to get XComponent id");
335   		    return;
336   	    }
337
338   	    std::string id(idStr);
339   	    auto render = PluginRender::GetInstance(id);
340   	    if (render != nullptr) {
341   		    // 封装OnSurfaceChanged方法
342   		    render->OnSurfaceChanged(component, window);
343   	    }
344    }
345
346    // 定义一个函数OnSurfaceDestroyedCB(),将PluginRender类内释放资源的方法Release()封装在其中
347    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
348   	    // ...
349   	    // 获取XComponent的id
350   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'};
351   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
352   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
353   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
354   			             "OnSurfaceDestroyedCB: Unable to get XComponent id");
355   		    return;
356   	    }
357
358   	    std::string id(idStr);
359   	    // 释放资源
360   	    PluginRender::Release(id);
361    }
362
363    // 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调
364    void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
365   	    // ...
366   	    // 获取XComponent的id
367   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
368   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
369   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
370   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
371   			             "DispatchTouchEventCB: Unable to get XComponent id");
372   		    return;
373   	    }
374
375   	    std::string id(idStr);
376   	    PluginRender *render = PluginRender::GetInstance(id);
377   	    if (render != nullptr) {
378   		    // 封装OnTouchEvent方法
379   		    render->OnTouchEvent(component, window);
380   	    }
381    }
382
383    // 定义一个函数DispatchMouseEventCB(),响应鼠标事件时触发该回调
384    void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) {
385   	    // ...
386   	    int32_t ret;
387   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
388   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
389   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
390   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
391   		    return;
392   	    }
393
394   	    std::string id(idStr);
395   	    auto render = PluginRender::GetInstance(id);
396   	    if (render) {
397   		    // 封装OnMouseEvent方法
398   		    render->OnMouseEvent(component, window);
399   	    }
400    }
401
402    // 定义一个函数DispatchHoverEventCB(),响应鼠标悬停事件时触发该回调
403    void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) {
404   	    // ...
405   	    int32_t ret;
406   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
407   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
408   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
409   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
410   		    return;
411   	    }
412
413   	    std::string id(idStr);
414   	    auto render = PluginRender::GetInstance(id);
415   	    if (render) {
416   		    // 封装OnHoverEvent方法
417   		    render->OnHoverEvent(component, isHover);
418   	    }
419    }
420
421    // 定义一个函数OnFocusEventCB(),响应获焦事件时触发该回调
422    void OnFocusEventCB(OH_NativeXComponent *component, void *window) {
423        // ...
424   	    int32_t ret;
425   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
426   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
427   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
428   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
429   		    return;
430   	    }
431
432   	    std::string id(idStr);
433   	    auto render = PluginRender::GetInstance(id);
434   	    if (render) {
435   		    // 封装OnFocusEvent方法
436   		    render->OnFocusEvent(component, window);
437   	    }
438    }
439
440    // 定义一个函数OnBlurEventCB(),响应失去焦点事件时触发该回调
441    void OnBlurEventCB(OH_NativeXComponent *component, void *window) {
442   	    // ...
443   	    int32_t ret;
444   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
445   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
446   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
447   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
448   	    	return;
449   	    }
450
451   	    std::string id(idStr);
452   	    auto render = PluginRender::GetInstance(id);
453   	    if (render) {
454   		    // 封装OnBlurEvent方法
455   		    render->OnBlurEvent(component, window);
456   	    }
457    }
458
459    // 定义一个函数OnKeyEventCB(),响应按键事件时触发该回调
460    void OnKeyEventCB(OH_NativeXComponent *component, void *window) {
461   	    // ...
462   	    int32_t ret;
463   	    char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
464   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
465   	    ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
466   	    if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
467   		    return;
468   	    }
469
470   	    std::string id(idStr);
471   	    auto render = PluginRender::GetInstance(id);
472   	    if (render) {
473   		    // 封装OnKeyEvent方法
474   		    render->OnKeyEvent(component, window);
475   	    }
476    }
477
478    // 定义一个OnSurfaceChanged()方法
479    void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) {
480        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
481   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
482   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
483   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceChanged: Unable to get XComponent id");
484   		    return;
485   	    }
486
487        std::string id(idStr);
488        PluginRender* render = PluginRender::GetInstance(id);
489        double offsetX;
490        double offsetY;
491        // 获取XComponent持有的surface相对其父组件左顶点的偏移量
492        OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY);
493        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset",
494                     "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY);
495        uint64_t width;
496        uint64_t height;
497        OH_NativeXComponent_GetXComponentSize(component, window, &width, &height);
498        if (render != nullptr) {
499            render->eglCore_->UpdateSize(width, height);
500        }
501    }
502
503    // 定义一个OnTouchEvent()方法
504    void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) {
505        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
506   	    uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
507   	    if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
508   		    OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
509   			             "OnTouchEvent: Unable to get XComponent id");
510   		    return;
511   	    }
512
513        OH_NativeXComponent_TouchEvent touchEvent;
514        // 获取由XComponent触发的触摸事件
515        OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
516        // 获取XComponent触摸点相对于XComponent组件左边缘的坐标x和相对于XComponent组件上边缘的坐标y
517        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", "touch info: x = %{public}lf, y = %{public}lf",
518                     touchEvent.x, touchEvent.y);
519        // 获取XComponent触摸点相对于XComponent所在应用窗口左上角的x坐标和相对于XComponent所在应用窗口左上角的y坐标
520        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
521                     "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY);
522        std::string id(idStr);
523        PluginRender* render = PluginRender::GetInstance(id);
524        if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NATIVEXCOMPONENT_UP) {
525            render->eglCore_->ChangeColor();
526            hasChangeColor_ = 1;
527        }
528        float tiltX = 0.0f;
529        float tiltY = 0.0f;
530        OH_NativeXComponent_TouchPointToolType toolType =
531            OH_NativeXComponent_TouchPointToolType::OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
532        // 获取XComponent触摸点的工具类型
533        OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType);
534        // 获取XComponent触摸点处相对X轴的倾斜角度
535        OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX);
536        // 获取XComponent触摸点处相对Y轴的倾斜角度
537        OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY);
538        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent",
539                     "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY);
540    }
541
542    // 定义一个OnMouseEvent()方法
543    void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) {
544        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent");
545        OH_NativeXComponent_MouseEvent mouseEvent;
546        // 获取由XComponent触发的鼠标事件
547        int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent);
548        if (ret == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
549   	        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
550                         "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d",
551                         mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button);
552        } else {
553   	        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error");
554        }
555    }
556
557    // 定义一个OnHoverEvent()方法
558    void PluginRender::OnHoverEvent(OH_NativeXComponent* component, bool isHover) {
559        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnHoverEvent isHover_ = %{public}d", isHover);
560    }
561
562    // 定义一个OnFocusEvent()方法
563    void PluginRender::OnFocusEvent(OH_NativeXComponent* component, void* window) {
564        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnFocusEvent");
565    }
566
567    // 定义一个OnBlurEvent()方法
568    void PluginRender::OnBlurEvent(OH_NativeXComponent* component, void* window) {
569        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnBlurEvent");
570    }
571
572    // 定义一个OnKeyEvent()方法
573    void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) {
574        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent");
575
576        OH_NativeXComponent_KeyEvent *keyEvent = nullptr;
577        // 获取由XComponent触发的按键事件
578        if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
579   	        OH_NativeXComponent_KeyAction action;
580            // 获取按键事件的动作
581   	        OH_NativeXComponent_GetKeyEventAction(keyEvent, &action);
582   	        OH_NativeXComponent_KeyCode code;
583            // 获取按键事件的键码值
584   	        OH_NativeXComponent_GetKeyEventCode(keyEvent, &code);
585   	        OH_NativeXComponent_EventSourceType sourceType;
586            // 获取按键事件的输入源类型
587   	        OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType);
588   	        int64_t deviceId;
589            // 获取按键事件的设备ID
590   	        OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId);
591   	        int64_t timeStamp;
592            // 获取按键事件的时间戳
593   	        OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp);
594   	        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender",
595                         "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, "
596                         "timeStamp=%{public}ld",
597                         action, code, sourceType, deviceId, timeStamp);
598        } else {
599   	        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error");
600        }
601    }
602    ```
603
604    (2) 注册XComponent事件回调函数,在XComponent事件触发时调用3.1步骤中定义的方法。
605
606    ```c++
607    void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) {
608        // 设置组件创建事件的回调函数,组件创建时触发相关操作,初始化环境与绘制背景
609        renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
610        // 设置组件改变事件的回调函数,组件改变时触发相关操作
611        renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
612        // 设置组件销毁事件的回调函数,组件销毁时触发相关操作,释放申请的资源
613        renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
614        // 设置触摸事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
615        renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
616        // 将OH_NativeXComponent_Callback注册给NativeXComponent
617        OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_);
618
619        // 设置鼠标事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
620        mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
621        // 设置鼠标悬停事件的回调函数,在触摸事件触发时调用Node-API接口函数,从而调用原C++方法
622        mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
623        // 将OH_NativeXComponent_MouseEvent_Callback注册给NativeXComponent
624        OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_);
625
626        // 将OnFocusEventCB方法注册给NativeXComponent
627        OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB);
628        // 将OnKeyEventCB方法注册给NativeXComponent
629        OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB);
630        // 将OnBlurEventCB方法注册给 NativeXComponent
631        OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB);
632    }
633    ```
634
635    (3) 定义NapiDrawPattern方法,暴露到ArkTS侧的drawPattern()方法会执行该方法。
636
637    ```c++
638    napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) {
639        // ...
640        // 获取环境变量参数
641        napi_value thisArg;
642        if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
643            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail");
644            return nullptr;
645        }
646
647        // 获取环境变量中XComponent实例
648        napi_value exportInstance;
649        if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) {
650            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
651                         "NapiDrawPattern: napi_get_named_property fail");
652            return nullptr;
653        }
654
655        // 通过napi_unwrap接口,获取XComponent的实例指针
656        OH_NativeXComponent *NativeXComponent = nullptr;
657        if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&NativeXComponent)) != napi_ok) {
658            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail");
659            return nullptr;
660        }
661
662        // 获取XComponent实例的id
663        char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
664        uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
665        if (OH_NativeXComponent_GetXComponentId(NativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
666            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender",
667                         "NapiDrawPattern: Unable to get XComponent id");
668            return nullptr;
669        }
670
671        std::string id(idStr);
672        PluginRender *render = PluginRender::GetInstance(id);
673        if (render) {
674            // 调用绘制方法
675            render->eglCore_->Draw();
676            OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed");
677        }
678        return nullptr;
679    }
680    ```
681
682    (4) 定义TestGetXComponentStatus方法,暴露到ArkTS侧的getStatus()方法会执行该方法。
683
684    ```c++
685    napi_value PluginRender::TestGetXComponentStatus(napi_env env, napi_callback_info info) {
686        napi_value hasDraw;
687        napi_value hasChangeColor;
688
689        napi_create_int32(env, hasDraw_, &(hasDraw));
690        napi_create_int32(env, hasChangeColor_, &(hasChangeColor));
691
692        napi_value obj;
693        napi_create_object(env, &obj);
694        napi_set_named_property(env, obj, "hasDraw", hasDraw);
695        napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
696
697        return obj;
698    }
699    ```
700
7014. 初始化环境,包括初始化可用的EGLDisplay、确定可用的surface配置、创建渲染区域surface、创建并关联上下文等。
702
703    ```c++
704    void EGLCore::UpdateSize(int width, int height) {
705        // width_和height_在头文件中定义
706        width_ = width;
707        height_ = height;
708    }
709
710    bool EGLCore::EglContextInit(void *window, int width, int height) {
711        // ...
712        UpdateSize(width, height);
713        eglWindow_ = static_cast<EGLNativeWindowType>(window);
714
715        // 初始化display
716        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
717        if (eglDisplay_ == EGL_NO_DISPLAY) {
718            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
719            return false;
720        }
721
722        // 初始化EGL
723        EGLint majorVersion;
724        EGLint minorVersion;
725        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
726            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
727                "eglInitialize: unable to get initialize EGL display");
728            return false;
729        }
730
731        // 选择配置
732        const EGLint maxConfigSize = 1;
733        EGLint numConfigs;
734        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
735            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
736            return false;
737        }
738
739        // 创建环境
740        return CreateEnvironment();
741    }
742    ```
743
744    ```c++
745    bool EGLCore::CreateEnvironment() {
746        // ...
747        // 创建surface
748        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
749
750        // ...
751        // 创建context
752        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
753        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
754            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
755            return false;
756        }
757
758        // 创建program
759        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
760        if (program_ == PROGRAM_ERROR) {
761            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
762            return false;
763        }
764        return true;
765    }
766
767    GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) {
768        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
769            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore",
770                         "createProgram: vertexShader or fragShader is null");
771            return PROGRAM_ERROR;
772        }
773
774        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
775        if (vertex == PROGRAM_ERROR) {
776            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error");
777            return PROGRAM_ERROR;
778        }
779
780        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
781        if (fragment == PROGRAM_ERROR) {
782            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error");
783            return PROGRAM_ERROR;
784        }
785
786        GLuint program = glCreateProgram();
787        if (program == PROGRAM_ERROR) {
788            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error");
789            glDeleteShader(vertex);
790            glDeleteShader(fragment);
791            return PROGRAM_ERROR;
792        }
793
794        // The gl function has no return value.
795        glAttachShader(program, vertex);
796        glAttachShader(program, fragment);
797        glLinkProgram(program);
798
799        GLint linked;
800        glGetProgramiv(program, GL_LINK_STATUS, &linked);
801        if (linked != 0) {
802            glDeleteShader(vertex);
803            glDeleteShader(fragment);
804            return program;
805        }
806
807        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error");
808        GLint infoLen = 0;
809        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
810        if (infoLen > 1) {
811            char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
812            memset(infoLog, 0, infoLen + 1);
813            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
814            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog);
815            free(infoLog);
816            infoLog = nullptr;
817        }
818        glDeleteShader(vertex);
819        glDeleteShader(fragment);
820        glDeleteProgram(program);
821        return PROGRAM_ERROR;
822    }
823
824    GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) {
825        if ((type <= 0) || (shaderSrc == nullptr)) {
826            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error");
827            return PROGRAM_ERROR;
828        }
829
830        GLuint shader = glCreateShader(type);
831        if (shader == 0) {
832            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader");
833            return PROGRAM_ERROR;
834        }
835
836        // The gl function has no return value.
837        glShaderSource(shader, 1, &shaderSrc, nullptr);
838        glCompileShader(shader);
839
840        GLint compiled;
841        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
842        if (compiled != 0) {
843            return shader;
844        }
845
846        GLint infoLen = 0;
847        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
848        if (infoLen <= 1) {
849            glDeleteShader(shader);
850            return PROGRAM_ERROR;
851        }
852
853        char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
854        if (infoLog != nullptr) {
855            memset(infoLog, 0, infoLen + 1);
856            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
857            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog);
858            free(infoLog);
859            infoLog = nullptr;
860        }
861        glDeleteShader(shader);
862        return PROGRAM_ERROR;
863    }
864
865    ```
866
8675. 渲染功能实现。
868
869   (1) 绘制背景。
870
871    ```c++
872    // ...
873    // 绘制背景颜色 #f4f4f4
874    const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
875
876    // 绘制图案颜色
877    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
878
879    // 绘制图案改变后的颜色
880    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
881
882    // 绘制背景顶点
883    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {
884        -1.0f, 1.0f,
885        1.0f, 1.0f,
886        1.0f, -1.0f,
887        -1.0f, -1.0f
888    };
889    // ...
890    ```
891
892    ```c++
893    // 绘制背景颜色
894    void EGLCore::Background() {
895        GLint position = PrepareDraw();
896        if (position == POSITION_ERROR) {
897            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed");
898            return;
899        }
900
901        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
902                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
903            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed");
904            return;
905        }
906
907        if (!FinishDraw()) {
908            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed");
909            return;
910        }
911    }
912
913    // 绘前准备,获取position,创建成功时position值从0开始
914    GLint EGLCore::PrepareDraw() {
915        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
916            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
917            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");
918            return POSITION_ERROR;
919        }
920
921        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
922        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
923        glClear(GL_COLOR_BUFFER_BIT);
924        glUseProgram(program_);
925
926        return glGetAttribLocation(program_, POSITION_NAME);
927    }
928
929    // 依据传入参数在指定区域绘制指定颜色
930    bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) {
931        if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) {
932            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
933            return false;
934        }
935
936        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
937        glEnableVertexAttribArray(position);
938        glVertexAttrib4fv(1, color);
939        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
940        glDisableVertexAttribArray(position);
941
942        return true;
943    }
944
945    // 结束绘制操作
946    bool EGLCore::FinishDraw() {
947        // 强制刷新缓冲
948        glFlush();
949        glFinish();
950        return eglSwapBuffers(eglDisplay_, eglSurface_);
951    }
952    ```
953
954   (2) 绘制图形。
955
956    ```c++
957    void EGLCore::Draw() {
958        flag_ = false;
959        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");
960        GLint position = PrepareDraw();
961        if (position == POSITION_ERROR) {
962            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");
963            return;
964        }
965
966        // 绘制背景
967        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
968                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
969            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");
970            return;
971        }
972
973        // 将五角星分为五个四边形,计算其中一个四边形的四个顶点
974        GLfloat rotateX = 0;
975        GLfloat rotateY = FIFTY_PERCENT * height_;
976        GLfloat centerX = 0;
977        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
978        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
979        GLfloat leftY = 0;
980        GLfloat rightX = rotateY * (M_PI / 180 * 18);
981        GLfloat rightY = 0;
982
983        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
984        const GLfloat shapeVertices[] = {
985            centerX / width_, centerY / height_,
986            leftX / width_, leftY / height_,
987            rotateX / width_, rotateY / height_,
988            rightX / width_, rightY / height_
989        };
990
991        if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
992            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
993            return;
994        }
995
996        GLfloat rad = M_PI / 180 * 72;
997        for (int i = 0; i < 4; ++i)
998        {
999            // 旋转得其他四个四边形的顶点
1000            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
1001            rotate2d(centerX, centerY, &leftX, &leftY,rad);
1002            rotate2d(centerX, centerY, &rightX, &rightY,rad);
1003
1004            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1005            const GLfloat shapeVertices[] = {
1006                    centerX / width_, centerY / height_,
1007                    leftX / width_, leftY / height_,
1008                    rotateX / width_, rotateY / height_,
1009                    rightX / width_, rightY / height_
1010            };
1011
1012            // 绘制图形
1013            if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
1014                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1015                return;
1016            }
1017        }
1018
1019        // 结束绘制
1020        if (!FinishDraw()) {
1021            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");
1022            return;
1023        }
1024
1025        flag_ = true;
1026    }
1027    ```
1028
1029   (3) 改变颜色,重新画一个大小相同颜色不同的图形,与原图形替换,达到改变颜色的效果。
1030
1031    ```c++
1032    void EGLCore::ChangeColor() {
1033        if (!flag_) {
1034            return;
1035        }
1036        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor");
1037        GLint position = PrepareDraw();
1038        if (position == POSITION_ERROR) {
1039            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");
1040            return;
1041        }
1042
1043        // 绘制背景
1044        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
1045                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
1046            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");
1047            return;
1048        }
1049
1050        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1051        GLfloat rotateX = 0;
1052        GLfloat rotateY = FIFTY_PERCENT * height_;
1053        GLfloat centerX = 0;
1054        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
1055        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
1056        GLfloat leftY = 0;
1057        GLfloat rightX = rotateY * (M_PI / 180 * 18);
1058        GLfloat rightY = 0;
1059
1060        // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1061        const GLfloat shapeVertices[] = {
1062            centerX / width_, centerY / height_,
1063            leftX / width_, leftY / height_,
1064            rotateX / width_, rotateY / height_,
1065            rightX / width_, rightY / height_
1066        };
1067
1068        // 使用新的颜色绘制
1069        if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
1070            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1071            return;
1072        }
1073
1074        GLfloat rad = M_PI / 180 * 72;
1075        for (int i = 0; i < 4; ++i) {
1076            // 旋转得其他四个四边形的顶点
1077            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
1078            rotate2d(centerX, centerY, &leftX, &leftY,rad);
1079            rotate2d(centerX, centerY, &rightX, &rightY,rad);
1080
1081            // 确定绘制四边形的顶点,使用绘制区域的百分比表示
1082            const GLfloat shapeVertices[] = {
1083                    centerX / width_, centerY / height_,
1084                    leftX / width_, leftY / height_,
1085                    rotateX / width_, rotateY / height_,
1086                    rightX / width_, rightY / height_
1087            };
1088
1089            // 使用新的颜色绘制
1090            if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
1091                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
1092                return;
1093            }
1094        }
1095
1096        // 结束绘制
1097        if (!FinishDraw()) {
1098            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");
1099        }
1100    }
1101
1102   bool EGLCore::ExecuteDrawNewStar(
1103       GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) {
1104       if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
1105           OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
1106           return false;
1107       }
1108
1109       // The gl function has no return value.
1110       glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
1111       glEnableVertexAttribArray(position);
1112       glVertexAttrib4fv(1, color);
1113       glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
1114       glDisableVertexAttribArray(position);
1115
1116       return true;
1117   }
1118    ```
1119
11206. 释放相关资源。
1121
1122    (1) EGLCore类下创建Release()方法,释放初始化环境时申请的资源,包含窗口display、渲染区域surface、环境上下文context等。
1123
1124    ```c++
1125    void EGLCore::Release() {
1126        // 释放surface
1127        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
1128            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");
1129        }
1130        // 释放context
1131        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
1132            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");
1133        }
1134        // 释放display
1135        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
1136            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");
1137        }
1138    }
1139    ```
1140
1141    (2) PluginRender类添加Release()方法,释放EGLCore实例及PluginRender实例。
1142
1143    ```c++
1144    void PluginRender::Release(std::string &id) {
1145        PluginRender *render = PluginRender::GetInstance(id);
1146        if (render != nullptr) {
1147            render->eglCore_->Release();
1148            delete render->eglCore_;
1149            render->eglCore_ = nullptr;
1150            instance_.erase(instance_.find(id));
1151        }
1152    }
1153    ```
1154
11557. CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
1156
1157    ```CMake
1158    # 设置CMake最小版本
1159    cmake_minimum_required(VERSION 3.4.1)
1160    # 项目名称
1161    project(XComponent)
1162
1163    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1164    add_definitions(-DOHOS_PLATFORM)
1165    # 设置头文件搜索目录
1166    include_directories(
1167        ${NATIVERENDER_ROOT_PATH}
1168        ${NATIVERENDER_ROOT_PATH}/include
1169    )
1170    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
1171    add_library(nativerender SHARED
1172        render/egl_core.cpp
1173        render/plugin_render.cpp
1174        manager/plugin_manager.cpp
1175        napi_init.cpp
1176    )
1177
1178    find_library(
1179        EGL-lib
1180        EGL
1181    )
1182
1183    find_library(
1184        GLES-lib
1185        GLESv3
1186    )
1187
1188    find_library(
1189        hilog-lib
1190        hilog_ndk.z
1191    )
1192
1193    find_library(
1194        libace-lib
1195        ace_ndk.z
1196    )
1197
1198    find_library(
1199        libnapi-lib
1200        ace_napi.z
1201    )
1202
1203    find_library(
1204        libuv-lib
1205        uv
1206    )
1207    # 添加构建需要链接的库
1208    target_link_libraries(nativerender PUBLIC
1209        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})
1210    ```
1211
1212## ArkTS XComponent场景
1213
1214与Native XComponent不同,ArkTS XComponent不再需要libraryname参数。通过在ArkTS侧获取SurfaceId,布局信息、生命周期回调、触摸、鼠标、按键等事件回调等均在ArkTS侧触发,按需传递到Native侧进行处理。主要开发场景如下:
1215- 基于ArkTS侧获取的SurfaceId,在Native侧调用OH_NativeWindow_CreateNativeWindowFromSurfaceId接口创建出NativeWindow实例。
1216- 利用NativeWindow和EGL接口开发自定义绘制内容以及申请和提交Buffer到图形队列。
1217- ArkTS侧获取生命周期、事件等信息传递到Native侧处理。
1218
1219> **说明**:
1220>
1221> 1. Native侧的NativeWindow缓存在字典中,其key需要保证其唯一性,当对应的XComponent销毁后,需要及时从字典里将其删除。
1222>
1223> 2. 对于使用[typeNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#typenode12)创建的SURFACE或TEXTURE类型的XComponent组件,由于typeNode组件的生命周期与声明式组件存在差异,组件在创建后的缓冲区尺寸为未设置状态,因此在开始绘制内容之前,应调用[OH_NativeWindow_NativeWindowHandleOpt](../reference/apis-arkgraphics2d/_native_window.md#oh_nativewindow_nativewindowhandleopt)接口进行缓冲区尺寸设置。
1224>
1225> 3. 多个XComponent开发时,缓存Native侧资源需要保证key是唯一的,key推荐使用id+随机数或者surfaceId。
1226
1227**接口说明**
1228
1229ArkTS侧的XComponentController
1230
1231| 接口名                                                       | 描述                                                         |
1232| ------------------------------------------------------------ | ------------------------------------------------------------ |
1233| getXComponentSurfaceId(): string                             | 获取XComponent对应Surface的ID。                                |
1234| onSurfaceCreated(surfaceId: string): void                    | 当XComponent持有的Surface创建后进行该回调。                    |
1235| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | 当XComponent持有的Surface大小改变后(包括首次创建时的大小改变)进行该回调。 |
1236| onSurfaceDestroyed(surfaceId: string): void                  | 当XComponent持有的Surface销毁后进行该回调。                    |
1237
1238Native侧
1239
1240| 接口名                                                       | 描述                                                         |
1241| ------------------------------------------------------------ | ------------------------------------------------------------ |
1242| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | 通过surfaceId创建对应的OHNativeWindow。                        |
1243| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | 将OHNativeWindow对象的引用计数减1,当引用计数为0的时候,该OHNativeWindow对象会被析构掉。 |
1244
1245**开发步骤**
1246
1247以下步骤以SURFACE类型为例,描述了如何使用`XComponent组件`在ArkTS侧传入SurfaceId,在native侧创建NativeWindow实例,然后创建`EGL/GLES`环境,实现在主页面绘制图形,并可以改变图形的颜色。
1248
12491. 在界面中定义XComponent。
1250
1251    ```javascript
1252    // 函数声明,在cpp/types/libnativerender/Index.d.ts中定义
1253    type XComponentContextStatus = {
1254        hasDraw: boolean,
1255        hasChangeColor: boolean,
1256    };
1257    export const SetSurfaceId: (id: BigInt) => any;
1258    export const ChangeSurface: (id: BigInt, w: number, h: number) =>any;
1259    export const DrawPattern: (id: BigInt) => any;
1260    export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus
1261    export const ChangeColor: (id: BigInt) => any;
1262    export const DestroySurface: (id: BigInt) => any;
1263    ```
1264
1265    ```typescript
1266    import nativeRender from 'libnativerender.so'
1267
1268    // 重写XComponentController,设置生命周期回调
1269    class MyXComponentController extends XComponentController {
1270        onSurfaceCreated(surfaceId: string): void {
1271            console.log(`onSurfaceCreated surfaceId: ${surfaceId}`)
1272            nativeRender.SetSurfaceId(BigInt(surfaceId));
1273        }
1274
1275        onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
1276            console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`)
1277            // 在onSurfaceChanged中调用ChangeSurface绘制内容
1278            nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight)
1279        }
1280
1281        onSurfaceDestroyed(surfaceId: string): void {
1282            console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`)
1283            nativeRender.DestroySurface(BigInt(surfaceId))
1284        }
1285    }
1286
1287    @Entry
1288    @Component
1289    struct Index {
1290        @State currentStatus: string = "index";
1291        xComponentController: XComponentController = new MyXComponentController();
1292
1293        build() {
1294            Column() {
1295                //...
1296                //在xxx.ets 中定义 XComponent
1297                Column({ space: 10 }) {
1298                    XComponent({
1299                        type: XComponentType.SURFACE,
1300                        controller: this.xComponentController
1301                    })
1302                Text(this.currentStatus)
1303                    .fontSize('24fp')
1304                    .fontWeight(500)
1305                }
1306                .onClick(() => {
1307                    let surfaceId = this.xComponentController.getXComponentSurfaceId()
1308                    nativeRender.ChangeColor(BigInt(surfaceId))
1309                    let hasChangeColor: boolean = false;
1310                    if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
1311                        hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor;
1312                    }
1313                    if (hasChangeColor) {
1314                        this.currentStatus = "change color";
1315                    }
1316                })
1317
1318                //...
1319                Row() {
1320                    Button('Draw Star')
1321                        .fontSize('16fp')
1322                        .fontWeight(500)
1323                        .margin({ bottom: 24 })
1324                        .onClick(() => {
1325                            let surfaceId = this.xComponentController.getXComponentSurfaceId()
1326                            nativeRender.DrawPattern(BigInt(surfaceId))
1327                            let hasDraw: boolean = false;
1328                            if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
1329                                hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw;
1330                            }
1331                            if (hasDraw) {
1332                                this.currentStatus = "draw star"
1333                            }
1334                        })
1335                        .width('53.6%')
1336                        .height(40)
1337                }
1338                .width('100%')
1339                .justifyContent(FlexAlign.Center)
1340                .alignItems(VerticalAlign.Bottom)
1341                .layoutWeight(1)
1342            }
1343            .width('100%')
1344            .height('100%')
1345        }
1346    }
1347    ```
1348
13492. Node-API模块注册,具体使用请参考[Native API在应用工程中的使用指导](../napi/napi-guidelines.md)。
1350
1351    ```typescript
1352    #include <hilog/log.h>
1353    #include "common/common.h"
1354    #include "manager/plugin_manager.h"
1355    namespace NativeXComponentSample {
1356    // 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用
1357    EXTERN_C_START
1358    static napi_value Init(napi_env env, napi_value exports) {
1359        // ...
1360        // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(),
1361        // DrawPattern(),ChangeColor(),GetXComponentStatus()
1362        napi_property_descriptor desc[] = {
1363            {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
1364            {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
1365            {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr},
1366            {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
1367            {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
1368            {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
1369             nullptr}};
1370        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
1371            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
1372            return nullptr;
1373        }
1374        return exports;
1375    }
1376    EXTERN_C_END
1377    // 编写接口的描述信息,根据实际需要可以修改对应参数
1378    static napi_module nativerenderModule = {.nm_version = 1,
1379                                             .nm_flags = 0,
1380                                             .nm_filename = nullptr,
1381                                             // 入口函数
1382                                             .nm_register_func = Init,
1383                                             // 模块名称
1384                                             .nm_modname = "nativerender",
1385                                             .nm_priv = ((void *)0),
1386                                             .reserved = {0}};
1387    } // namespace NativeXComponentSample
1388    // __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册
1389    extern "C" __attribute__((constructor)) void RegisterModule(void) {
1390        napi_module_register(&NativeXComponentSample::nativerenderModule);
1391    }
1392    ```
1393
13943. 上述注册的六个函数在native侧具体实现。
1395
1396    ```cpp
1397    // PluginManager类定义
1398    class PluginManager {
1399    public:
1400        ~PluginManager();
1401        static PluginRender *GetPluginRender(int64_t &id);
1402        static napi_value ChangeColor(napi_env env, napi_callback_info info);
1403        static napi_value DrawPattern(napi_env env, napi_callback_info info);
1404        static napi_value SetSurfaceId(napi_env env, napi_callback_info info);
1405        static napi_value ChangeSurface(napi_env env, napi_callback_info info);
1406        static napi_value DestroySurface(napi_env env, napi_callback_info info);
1407        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
1408
1409    public:
1410        static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_;
1411        static std::unordered_map<int64_t, OHNativeWindow *> windowMap_;
1412    };
1413
1414    // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值
1415    int64_t ParseId(napi_env env, napi_callback_info info) {
1416        if ((env == nullptr) || (info == nullptr)) {
1417            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null");
1418            return -1;
1419        }
1420        size_t argc = 1;
1421        napi_value args[1] = {nullptr};
1422        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
1423            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed");
1424            return -1;
1425        }
1426        int64_t value = 0;
1427        bool lossless = true;
1428        if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) {
1429            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed");
1430            return -1;
1431        }
1432        return value;
1433    }
1434
1435    // 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化
1436    napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) {
1437        int64_t surfaceId = ParseId(env, info);
1438        OHNativeWindow *nativeWindow;
1439        PluginRender *pluginRender;
1440        if (windowMap_.find(surfaceId) == windowMap_.end()) {
1441            OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
1442            windowMap_[surfaceId] = nativeWindow;
1443        }
1444        if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
1445            pluginRender = new PluginRender(surfaceId);
1446            pluginRenderMap_[surfaceId] = pluginRender;
1447        }
1448        pluginRender->InitNativeWindow(nativeWindow);
1449        return nullptr;
1450    }
1451
1452    void PluginRender::InitNativeWindow(OHNativeWindow *window) {
1453        eglCore_->EglContextInit(window); // 参考Native XComponent场景 EglContextInit的实现
1454    }
1455
1456    // 根据传入的surfaceId、width、height实现surface大小的变动
1457    napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) {
1458        if ((env == nullptr) || (info == nullptr)) {
1459            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1460                         "ChangeSurface: OnLoad env or info is null");
1461            return nullptr;
1462        }
1463        int64_t surfaceId = 0;
1464        size_t argc = 3;
1465        napi_value args[3] = {nullptr};
1466
1467        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
1468            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1469                         "ChangeSurface: GetContext napi_get_cb_info failed");
1470            return nullptr;
1471        }
1472        bool lossless = true;
1473        int index = 0;
1474        if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) {
1475            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed");
1476            return nullptr;
1477        }
1478        double width;
1479        if (napi_ok != napi_get_value_double(env, args[index++], &width)) {
1480            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed");
1481            return nullptr;
1482        }
1483        double height;
1484        if (napi_ok != napi_get_value_double(env, args[index++], &height)) {
1485            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed");
1486            return nullptr;
1487        }
1488        auto pluginRender = GetPluginRender(surfaceId);
1489        if (pluginRender == nullptr) {
1490            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed");
1491            return nullptr;
1492        }
1493        pluginRender->UpdateNativeWindowSize(width, height);
1494        return nullptr;
1495    }
1496
1497    void PluginRender::UpdateNativeWindowSize(int width, int height) {
1498        eglCore_->UpdateSize(width, height); // 参考Native XComponent场景 UpdateSize的实现
1499        if (!hasChangeColor_ && !hasDraw_) {
1500            eglCore_->Background(); // 参考Native XComponent场景 Background的实现
1501        }
1502    }
1503
1504    // 销毁surface
1505    napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) {
1506        int64_t surfaceId = ParseId(env, info);
1507        auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId);
1508        if (pluginRenderMapIter != pluginRenderMap_.end()) {
1509            delete pluginRenderMapIter->second;
1510            pluginRenderMap_.erase(pluginRenderMapIter);
1511        }
1512        auto windowMapIter = windowMap_.find(surfaceId);
1513        if (windowMapIter != windowMap_.end()) {
1514            OH_NativeWindow_DestroyNativeWindow(windowMapIter->second);
1515            windowMap_.erase(windowMapIter);
1516        }
1517        return nullptr;
1518    }
1519
1520    // 实现EGL绘画逻辑
1521    napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) {
1522        int64_t surfaceId = ParseId(env, info);
1523        auto pluginRender = GetPluginRender(surfaceId);
1524        if (pluginRender == nullptr) {
1525            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed");
1526            return nullptr;
1527        }
1528        pluginRender->DrawPattern();
1529        return nullptr;
1530    }
1531
1532    PluginRender *PluginManager::GetPluginRender(int64_t &id) {
1533        if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) {
1534            return pluginRenderMap_[id];
1535        }
1536        return nullptr;
1537    }
1538
1539    void PluginRender::DrawPattern() {
1540        eglCore_->Draw(hasDraw_); // 参考Native XComponent场景 Draw实现
1541    }
1542
1543    // 实现改变绘制图形颜色的功能
1544    napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) {
1545        int64_t surfaceId = ParseId(env, info);
1546        auto pluginRender = GetPluginRender(surfaceId);
1547        if (pluginRender == nullptr) {
1548            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed");
1549            return nullptr;
1550        }
1551        pluginRender->ChangeColor(); // 参考Native XComponent场景 ChangeColor实现
1552        return nullptr;
1553    }
1554
1555    void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
1556
1557    // 获得xcomponent状态,并返回至ArkTS侧
1558    napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) {
1559        int64_t surfaceId = ParseId(env, info);
1560        auto pluginRender = GetPluginRender(surfaceId);
1561        if (pluginRender == nullptr) {
1562            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1563                         "GetXComponentStatus: Get pluginRender failed");
1564            return nullptr;
1565        }
1566        napi_value hasDraw;
1567        napi_value hasChangeColor;
1568        napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw));
1569        if (ret != napi_ok) {
1570            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1571                         "GetXComponentStatus: napi_create_int32 hasDraw_ error");
1572            return nullptr;
1573        }
1574        ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor));
1575        if (ret != napi_ok) {
1576            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1577                         "GetXComponentStatus: napi_create_int32 hasChangeColor_ error");
1578            return nullptr;
1579        }
1580        napi_value obj;
1581        ret = napi_create_object(env, &obj);
1582        if (ret != napi_ok) {
1583            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1584                         "GetXComponentStatus: napi_create_object error");
1585            return nullptr;
1586        }
1587        ret = napi_set_named_property(env, obj, "hasDraw", hasDraw);
1588        if (ret != napi_ok) {
1589            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1590                         "GetXComponentStatus: napi_set_named_property hasDraw error");
1591            return nullptr;
1592        }
1593        ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
1594        if (ret != napi_ok) {
1595            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1596                         "GetXComponentStatus: napi_set_named_property hasChangeColor error");
1597            return nullptr;
1598        }
1599        return obj;
1600    }
1601
1602    int32_t PluginRender::HasDraw() { return hasDraw_; }
1603
1604    int32_t PluginRender::HasChangedColor() { return hasChangeColor_; }
1605    ```
1606
16074. 配置具体的CMakeLists,使用CMake工具链将C++源代码编译成动态链接库文件。
1608
1609    ```cmake
1610    # 设置CMake最小版本.
1611    cmake_minimum_required(VERSION 3.4.1)
1612    # 项目名称
1613    project(XComponent)
1614
1615    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1616    add_definitions(-DOHOS_PLATFORM)
1617    # 设置头文件搜索目录
1618    include_directories(
1619        ${NATIVERENDER_ROOT_PATH}
1620        ${NATIVERENDER_ROOT_PATH}/include
1621    )
1622    # 添加名为nativerender的动态库,库文件名为libnativerender.so,添加cpp文件
1623    add_library(nativerender SHARED
1624        render/egl_core.cpp
1625        render/plugin_render.cpp
1626        manager/plugin_manager.cpp
1627        napi_init.cpp
1628    )
1629
1630    find_library(
1631        # Sets the name of the path variable.
1632        EGL-lib
1633        # Specifies the name of the NDK library that
1634        # you want CMake to locate.
1635        EGL
1636    )
1637
1638    find_library(
1639        # Sets the name of the path variable.
1640        GLES-lib
1641        # Specifies the name of the NDK library that
1642        # you want CMake to locate.
1643        GLESv3
1644    )
1645
1646    find_library(
1647        # Sets the name of the path variable.
1648        hilog-lib
1649        # Specifies the name of the NDK library that
1650        # you want CMake to locate.
1651        hilog_ndk.z
1652    )
1653
1654    find_library(
1655        # Sets the name of the path variable.
1656        libace-lib
1657        # Specifies the name of the NDK library that
1658        # you want CMake to locate.
1659        ace_ndk.z
1660    )
1661
1662    find_library(
1663        # Sets the name of the path variable.
1664        libnapi-lib
1665        # Specifies the name of the NDK library that
1666        # you want CMake to locate.
1667        ace_napi.z
1668    )
1669
1670    find_library(
1671        # Sets the name of the path variable.
1672        libuv-lib
1673        # Specifies the name of the NDK library that
1674        # you want CMake to locate.
1675        uv
1676    )
1677    # 添加构建需要链接的库
1678    target_link_libraries(nativerender PUBLIC
1679        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
1680    ```
1681
1682## 自绘制原理说明
1683
1684XComponent持有一个surface,开发者能通过调用NativeWindow等接口,申请并提交Buffer至图形队列,以此方式将自绘制内容传送至该surface。XComponent负责将此surface整合进UI界面,其中展示的内容正是开发者传送的自绘制内容。surface的默认位置与大小与XComponent组件一致,开发者可利用[setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12)接口自定义调整surface的位置和大小。
1685
1686> **说明:**
1687>
1688> 当开发者传输的绘制内容包含透明元素时,surface区域的显示效果会与下方内容进行合成展示。例如,若传输的内容完全透明,且XComponent的背景色被设置为黑色,同时Surface保持默认的大小与位置,则最终显示的将是一片黑色区域。
1689
1690## 生命周期说明
1691
1692开发者在ArkTS侧使用如下代码即可用XComponent组件进行利用EGL/OpenGLES渲染的开发。
1693
1694```typescript
1695@Builder
1696function myComponent() {
1697  XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' })
1698    .onLoad((context) => {})
1699    .onDestroy(() => {})
1700}
1701```
1702
1703### onLoad事件
1704
1705使用场景:当需要用到native侧注册的方法,可以考虑调用。
1706
1707触发时刻:XComponent准备好surface后触发。
1708
1709参数context:其上面挂载了暴露在模块上的Native方法,使用方法类似于利用 import context from "libnativerender.so" 直接加载模块后获得的context实例。
1710
1711**时序**:
1712
1713​	Native XComponent场景:
1714
1715​	onLoad事件的触发和surface相关,其和Native侧的OnSurfaceCreated的时序如下图:
1716
1717![onLoad](./figures/onLoad.png)
1718
1719​	ArkTS XComponent场景:
1720
1721​	onLoad事件的触发和surface相关,其和ArkTS侧的OnSurfaceCreated的时序如下图:
1722
1723![onLoad](./figures/onLoad1.png)
1724
1725
1726
1727### onDestroy事件
1728
1729触发时刻:XComponent组件被销毁时触发,与一般ArkUI的组件销毁时机一致。
1730
1731使用场景:和onLoad事件对应,如果在onLoad申请了内存,可以在这里进行释放。
1732
1733**时序:**
1734
1735​	Native XComponent场景:
1736
1737​	onDestroy事件的触发和surface相关,其和Native侧的OnSurfaceDestroyed的时序如下图:
1738
1739![onDestroy](./figures/onDestroy.png)
1740
1741
1742
1743​	ArkTS XComponent场景:
1744
1745​	onDestroy事件的触发和surface相关,其和ArkTS侧的OnSurfaceDestroyed的时序如下图:
1746
1747![onDestroy](./figures/onDestroy1.png)
1748
1749<!--RP1--><!--RP1End-->
1750
1751## 相关实例
1752
1753针对Native XComponent的使用,有以下相关实例可供参考:
1754
1755- [XComponent3D(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent3D)
1756- [Native XComponent(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/XComponent)
1757- [OpenGL三棱椎(API10)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/NdkOpenGL)
1758
1759针对ArkTS XComponent的使用,有以下相关实例可供参考:
1760
1761- [ArkTSXComponent(API12)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Native/ArkTSXComponent)
1762