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