1# Custom Rendering (XComponent)
2
3## Overview
4
5The **XComponent** is a rendering component that can be used for EGL/OpenGL ES and media data output. It uses a unique [NativeWindow](../graphics/native-window-guidelines.md) to render graphics and is typically employed to meet complex custom rendering needs, such as displaying camera preview streams and rendering game graphics. You can specify different rendering methods through the **type** field, which are [XComponentType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#xcomponenttype10).SURFACE and XComponentType.TEXTURE. For the SURFACE type, you display the custom drawing content on the screen separately. For the TEXTURE type, you combine custom drawing content with the content of the **XComponent** and display it on the screen.
6
7The **XComponent** is mainly used in two scenarios. In the native XComponent scenario, the native layer is responsible for obtaining the native **XComponent** instance and registering the lifecycle callbacks of the **XComponent** along with touch, mouse, and key event callbacks. In the ArkTS XComponent scenario, the **SurfaceId** is obtained on the ArkTS side, with lifecycle callbacks, touch, mouse, and key event callbacks all being managed and triggered there.
8
9## Native XComponent Scenario
10Specify the dynamic library to be loaded with the **libraryname** parameter of the **XComponent** constructor. The application can then obtain the native **XComponent** instance at the native layer. This instance, provided by the **XComponent** itself, acts as a bridge, facilitating interactions between the ArkTS layer and the native layer. The NDK APIs provided by the **XComponent** all depend on this instance. The provided APIs include those for obtaining a **NativeWindow** instance, obtaining the layout or event information of the **XComponent**, registering the lifecycle callbacks of the **XComponent**, and registering the callbacks for the touch, mouse, and key events of the **XComponent**. You can use the provided APIs in the following scenarios:
11
12- Register the lifecycle and event callbacks of the **XComponent**.
13- Initialize the environment, obtain the current state, and respond to various events via these callbacks.
14- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue.
15
16**Available APIs**
17
18| API                                                      | Description                                                        |
19| ------------------------------------------------------------ | ------------------------------------------------------------ |
20| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | Obtains the ID of an **XComponent**.                                        |
21| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | Obtains the size of the surface held by an **XComponent**.                         |
22| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | Obtains the offset of the surface held by the **XComponent** relative to the upper left corner of its parent component.     |
23| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | Obtains the touch event triggered by an **XComponent**. For details about the attribute values in **touchEvent**, see [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) | Obtains the tool type of an **XComponent** touch point.                            |
25| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | Obtains the tilt of an **XComponent** touch point relative to the x-axis.                   |
26| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | Obtains the tilt of an **XComponent** touch point relative to the y-axis.                   |
27| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | Obtains the mouse event triggered by an **XComponent**.                            |
28| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | Registers a lifecycle or touch event callback for an **OH_NativeXComponent** instance.     |
29| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | Registers the mouse event callback for an **OH_NativeXComponent** instance.               |
30| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus obtaining event callback for an **OH_NativeXComponent** instance.           |
31| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the key event callback for an **OH_NativeXComponent** instance.               |
32| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus loss event callback for an **OH_NativeXComponent** instance.           |
33| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | Obtains the key event triggered by an **XComponent**.                            |
34| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | Obtains the action of a key event.                                        |
35| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | Obtains the key code value of a key event.                                      |
36| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | Obtains the input source type of a key event.                                  |
37| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | Obtains the device ID of a key event.                                      |
38| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | Obtains the timestamp of a key event.                                      |
39
40> **NOTE**
41>
42> The preceding APIs do not support cross-thread access.
43>
44> When the XComponent is destroyed (after the **onSurfaceDestroyed** callback is triggered), the **OH_NativeXComponent** and window objects obtained through the preceding APIs will be released. If these object are used again, crashes may occur due to the use of dangling or null pointers.
45
46**How to Develop**
47
48The following uses the SURFACE type as an example to describe how to use the **XComponent** to call the Node-API to create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color.
49
501. Define the **XComponent** on the UI.
51
52    ```typescript
53    // API declaration
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            // Define XComponent in an .ets file.
82            XComponent(this.xComponentAttrs)
83                .focusable(true) // Set the component to be able to respond to key events.
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. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md).
104
105    ```c++
106    // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call.
107    EXTERN_C_START
108    static napi_value Init(napi_env env, napi_value exports)
109    {
110        // ...
111        // Expose the getContext() API to the ArkTS code.
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        // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API.
120        PluginManager::GetInstance()->Export(env, exports);
121        return exports;
122    }
123    EXTERN_C_END
124
125    // Write the API description. You can modify parameters as required.
126    static napi_module nativerenderModule = {
127        .nm_version = 1,
128        .nm_flags = 0,
129        .nm_filename = nullptr,
130        // Entry point function
131        .nm_register_func = Init,// Specify the callback for when the corresponding module is loaded.
132        // Module name
133        .nm_modname = "nativerender", // Specify the module name. For XComponent-related development, the name must be the same as the value of libraryname in the XComponent on ArkTS.
134        .nm_priv = ((void *)0),
135        .reserved = { 0 }
136    };
137
138    // The method decorated by __attribute__((constructor)) is automatically called by the system. The Node-API napi_module_register() is used to pass in the module description for module registration.
139    extern "C" __attribute__((constructor)) void RegisterModule(void)
140    {
141        napi_module_register(&nativerenderModule);
142    }
143    ```
144    ```c++
145    // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API.
146    void PluginManager::Export(napi_env env, napi_value exports)
147    {
148        // ...
149        // Obtain a native XComponent.
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        // Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code.
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                // Register the callbacks.
172                render->RegisterCallback(nativeXComponent);
173                // Use Node-API in the method to export drawing-related APIs to expose them to the ArkTS side.
174                render->Export(env, exports);
175            }
176        }
177    }
178    ```
179    ```c++
180    // Use the napi_define_properties method to expose the drawPattern() method to the ArkTS side, which allows the drawPattern() method to be called in ArkTS code.
181    void PluginRender::Export(napi_env env, napi_value exports)
182    {
183        // ...
184        // Register the function as the ArkTS API 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. Register the **XComponent** event callback and use the Node-API to implement it.
195
196   (1) Define the callbacks for the touch event of the **XComponent** and for when a surface is successfully created, changed, or destroyed.
197
198   ```c++
199   // Define the PluginRender class.
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   // Define the OnSurfaceCreatedCB() function to encapsulate the initialization environment and drawing background.
251   void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window)
252   {
253   	// ...
254   	// Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code.
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   	// Initialize the environment and draw the background.
264   	std::string id(idStr);
265   	auto render = PluginRender::GetInstance(id);
266   	uint64_t width;
267   	uint64_t height;
268   	// Obtain the size of the surface held by the XComponent.
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   // Define the OnSurfaceChangedCB() function.
278   void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window)
279   {
280   	// ...
281   	// Obtain the ID of the XComponent.
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   		// Encapsulate the OnSurfaceChanged method.
294   		render->OnSurfaceChanged(component, window);
295   	}
296   }
297
298   // Define the OnSurfaceDestroyedCB() function and encapsulate in it the Release() method in the PluginRender class for releasing resources.
299   void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window)
300   {
301   	// ...
302   	// Obtain the ID of the XComponent.
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   	// Release resources.
313   	PluginRender::Release(id);
314   }
315
316   // Define the DispatchTouchEventCB() function, which is triggered to respond to a touch event.
317   void DispatchTouchEventCB(OH_NativeXComponent *component, void *window)
318   {
319   	// ...
320   	// Obtain the ID of the XComponent.
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   		// Encapsulate the OnTouchEvent method.
333   		render->OnTouchEvent(component, window);
334   	}
335   }
336
337   // Define the DispatchMouseEventCB() function, which is triggered when a mouse event is responded to.
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   		// Encapsulate the OnMouseEvent method.
352   		render->OnMouseEvent(component, window);
353   	}
354   }
355
356   // Define the DispatchHoverEventCB() function, which is triggered when the mouse pointer hover event is responded to.
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   		// Encapsulate the OnHoverEvent method.
371   		render->OnHoverEvent(component, isHover);
372   	}
373   }
374
375   // Define the OnFocusEventCB() function, which is triggered when a focus obtaining event is responded to.
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   		// Encapsulate the OnFocusEvent method.
390   		render->OnFocusEvent(component, window);
391   	}
392   }
393
394   // Define the OnBlurEventCB() function, which is triggered when the focus loss event is responded to.
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   		// Encapsulate the OnBlurEvent method.
409   		render->OnBlurEvent(component, window);
410   	}
411   }
412
413   // Define the OnKeyEventCB() function, which is triggered when a key event is responded to.
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   		// Encapsulate the OnKeyEvent method.
427   		render->OnKeyEvent(component, window);
428   	}
429   }
430
431   // Define an OnSurfaceChanged() method.
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       // Obtain the offset of the surface held by the XComponent relative to the upper left corner of its parent component.
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   // Define an OnTouchEvent() method.
452   void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window)
453   {
454       // ...
455       OH_NativeXComponent_TouchEvent touchEvent;
456       // Obtain the touch event triggered by the XComponent.
457       OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent);
458       // Obtain the x coordinate of the XComponent touch point relative to the left edge of the XComponent and the y coordinate of the XComponent touch point relative to the upper edge of the XComponent.
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       // Obtain the x coordinate and y-coordinate of the XComponent touch point relative to the upper left corner of the application window where the XComponent is located.
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       // Obtain the tool type of the XComponent touch point.
475       OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType);
476       // Obtain the tilt of the XComponent touch point relative to the x-axis.
477       OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX);
478       // Obtain the tilt of the XComponent touch point relative to the y-axis.
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   // Define an OnMouseEvent() method.
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      // Obtain the mouse event triggered by the 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   // Define an OnHoverEvent() method.
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   // Define an OnFocusEvent() method.
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   // Define an OnBlurEvent() method.
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   // Define an OnKeyEvent() method.
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      // Obtain the key event triggered by the XComponent.
521      if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) {
522   	   OH_NativeXComponent_KeyAction action;
523          // Obtain the action of a key event.
524   	   OH_NativeXComponent_GetKeyEventAction(keyEvent, &action);
525   	   OH_NativeXComponent_KeyCode code;
526          // Obtain the key code value of a key event.
527   	   OH_NativeXComponent_GetKeyEventCode(keyEvent, &code);
528   	   OH_NativeXComponent_EventSourceType sourceType;
529          // Obtain the input source type of a key event.
530   	   OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType);
531   	   int64_t deviceId;
532          // Obtain the device ID of a key event.
533   	   OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId);
534   	   int64_t timeStamp;
535          // Obtain the timestamp of a key event.
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) Register the **XComponent** event callback and call the method defined in step 3.1 when the **XComponent** event is triggered.
545
546    ```c++
547    void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) {
548        // Set the callback of the component creation event. When the component is created, related operations are triggered to initialize the environment and draw the background.
549        renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB;
550        // Set the callback of the component change event. When the component changes, related operations are triggered.
551        renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB;
552        // Set the callback of the component destruction event. When the component is destroyed, related operations are triggered to release the requested resources.
553        renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
554        // Set the callback of the touch event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method.
555        renderCallback_.DispatchTouchEvent = DispatchTouchEventCB;
556        // Register OH_NativeXComponent_Callback with NativeXComponent.
557        OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_);
558
559        // Set the callback of the mouse event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method.
560        mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB;
561        // Set the callback of the mouse hover event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method.
562        mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB;
563        // Register OH_NativeXComponent_MouseEvent_Callback with NativeXComponent.
564        OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_);
565
566        // Register the OnFocusEventCB method with NativeXComponent.
567        OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB);
568        // Register the OnKeyEventCB method with NativeXComponent.
569        OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB);
570        // Register the OnBlurEventCB method with NativeXComponent.
571        OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB);
572    }
573    ```
574
575   (3) Define the **NapiDrawPattern** method, which will be called by the **drawPattern()** method exposed to the ArkTS side.
576
577    ```c++
578    napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info)
579    {
580        // ...
581        // Obtain environment variables.
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        // Obtain the XComponent instance from the environment variables.
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        // Use napi_unwrap to obtain the pointer to the XComponent instance.
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        // Obtain the ID of the XComponent.
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            // Call the drawing method.
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. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering area surface, and creating and associating the context.
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        // Initialize the 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        // Initialize the 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        // Select the configuration.
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        // Create an environment.
663        return CreateEnvironment();
664    }
665    ```
666
667    ```c++
668    bool EGLCore::CreateEnvironment()
669    {
670        // ...
671        // Create a surface.
672        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
673
674        // ...
675        // Create a 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        // Create a 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. Implement the rendering function.
794
795   (1) Draw the background.
796
797    ```c++
798    // Draw the background color #f4f4f4.
799    const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
800
801    // Draw the background vertex.
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    // Draw the background color.
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    // Prepare for drawing and obtain the value of position. When the creation is successful, the value of position starts from 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    // Draw a specified color in the specified area based on the input parameters.
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    // End the drawing operation.
868    bool EGLCore::FinishDraw()
869    {
870        // Forcibly flush the buffer.
871        glFlush();
872        glFinish();
873        return eglSwapBuffers(eglDisplay_, eglSurface_);
874    }
875    ```
876
877   (2) Draw the shape.
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        // Draw the background.
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        // Divide the pentagon into five quadrilaterals and calculate the four vertices of one of the quadrilaterals.
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        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
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            // Obtain the vertices of the other four quadrilaterals through rotation.
924            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
925            rotate2d(centerX, centerY, &leftX, &leftY,rad);
926            rotate2d(centerX, centerY, &rightX, &rightY,rad);
927
928            // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
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            // Draw the shape.
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        // End drawing.
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) Change the colors, by drawing a new shape with the same size but different colors and replacing the original shape with the new shape.
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        // Draw the background.
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        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
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        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
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        // Use the new colors for drawing.
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            // Obtain the vertices of the other four quadrilaterals through rotation.
1003            rotate2d(centerX, centerY, &rotateX, &rotateY,rad);
1004            rotate2d(centerX, centerY, &leftX, &leftY,rad);
1005            rotate2d(centerX, centerY, &rightX, &rightY,rad);
1006
1007            // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
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            // Use the new colors for drawing.
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        // End drawing.
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. Release related resources.
1048
1049   (1) Create the **Release()** method in the **EGLCore** class to release the resources requested during environment initialization, including the window display, rendering area surface, and environment context.
1050
1051    ```c++
1052    void EGLCore::Release()
1053    {
1054        // Release the 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        // Release the 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        // Release the 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) Add the **Release()** method to the **PluginRender** class to release the **EGLCore** and **PluginRender** instances.
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. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file.
1087
1088    ```CMake
1089    # Set the minimum CMake version.
1090    cmake_minimum_required(VERSION 3.4.1)
1091    # Project name
1092    project(XComponent)
1093
1094    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1095    add_definitions(-DOHOS_PLATFORM)
1096    # Set the header file search directory.
1097    include_directories(
1098        ${NATIVERENDER_ROOT_PATH}
1099        ${NATIVERENDER_ROOT_PATH}/include
1100    )
1101    # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files.
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    # Add the libraries to be linked.
1139    target_link_libraries(nativerender PUBLIC
1140        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})
1141    ```
1142
1143## ArkTS XComponent Scenario
1144
1145Unlike the native **XComponent**, the ArkTS **XComponent** does not require the **libraryname** parameter. It obtains the **SurfaceId** on the ArkTS side, and layout information, lifecycle callbacks, touch, mouse, key event callbacks, and more are all triggered on the ArkTS side and passed to the native side for processing as needed. The development mainly involves the following scenarios:
1146- Use the **SurfaceId** obtained on the ArkTS side to call the **OH_NativeWindow_CreateNativeWindowFromSurfaceId** API on the native side to create a **NativeWindow** instance.
1147- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue.
1148- Obtain lifecycle and event information on the ArkTS side and pass it to the native side for processing.
1149
1150**Available APIs**
1151
1152XComponentController on the ArkTS side
1153
1154| API                                                      | Description                                                        |
1155| ------------------------------------------------------------ | ------------------------------------------------------------ |
1156| getXComponentSurfaceId(): string                             | Obtains the ID of the surface associated with the **XComponent**.                               |
1157| onSurfaceCreated(surfaceId: string): void                    | Called when the surface held by the **XComponent** is created.                   |
1158| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | Called when the size of the surface held by the **XComponent** changes (including the initial size change upon creation).|
1159| onSurfaceDestroyed(surfaceId: string): void                  | Called when the surface held by the **XComponent** is destroyed.                   |
1160
1161Native side
1162
1163| API                                                      | Description                                                        |
1164| ------------------------------------------------------------ | ------------------------------------------------------------ |
1165| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | Creates an **OHNativeWindow** instance based on a surface ID.                       |
1166| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | Decreases the reference count of an **OHNativeWindow** instance by 1 and when the reference count reaches 0, destroys the instance.|
1167
1168**How to Develop**
1169
1170The following uses the SURFACE type as an example to describe how to use the **XComponent** to pass in **SurfaceId** on the ArkTS side, create a **NativeWindow** instance on the native side, then create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color.
1171
11721. Define the **XComponent** on the UI.
1173
1174    ```javascript
1175    // Function declaration
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    // Override 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          // Define XComponent in an .ets file.
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. Register the Node-API module. For details, see [Node-API Development Specifications](../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    // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call.
1279    EXTERN_C_START
1280    static napi_value Init(napi_env env, napi_value exports) {
1281        // ...
1282        // Expose the SetSurfaceId(), ChangeSurface(), and DestroySurface() APIs to the ArkTS side.
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    // Write the API description. You can modify parameters as required.
1300    static napi_module nativerenderModule = {.nm_version = 1,
1301                                             .nm_flags = 0,
1302                                             .nm_filename = nullptr,
1303                                             // Entry point function
1304                                             .nm_register_func = Init,
1305                                             // Module name
1306                                             .nm_modname = "nativerender",
1307                                             .nm_priv = ((void *)0),
1308                                             .reserved = {0}};
1309    } // namespace NativeXComponentSample
1310    // The method decorated by __attribute__((constructor)) is automatically called by the system. The Node-API napi_module_register() is used to pass in the module description for module registration.
1311    extern "C" __attribute__((constructor)) void RegisterModule(void) {
1312        napi_module_register(&NativeXComponentSample::nativerenderModule);
1313    }
1314    ```
1315
13163. Implement the preceding six registered functions on the native side.
1317
1318    ```cpp
1319    // Define the PluginManager class.
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    // Parse the surfaceId passed from ArkTS. Here, surfaceId is a 64-bit integer value.
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    // Set SurfaceId and initialize NativeWindow based on SurfaceId.
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); // For details about the EglContextInit implementation, see the "Native XComponent Scenario" section.
1375    }
1376
1377    // Implement surface size changes based on the passed surfaceId, width, and height.
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); // For details about the UpdateSize implementation, see the "Native XComponent Scenario" section.
1420        if (!hasChangeColor_ && !hasDraw_) {
1421            eglCore_->Background(); // For details about the Background implementation, see the "Native XComponent Scenario" section.
1422        }
1423    }
1424
1425    // Destroy the 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    // Implement the EGL drawing logic.
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_); // For details about the Draw implementation, see the "Native XComponent Scenario" section.
1460    }
1461
1462    // Implement the feature of changing the color of the drawn graphics.
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 (); // For details about the ChangeColor implementation, see the "Native XComponent Scenario" section.
1471        return nullptr;
1472    }
1473    void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
1474
1475    // Obtain the XComponent status and return it to the ArkTS side.
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. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file.
1524
1525    ```cmake
1526    # Set the minimum CMake version.
1527    cmake_minimum_required(VERSION 3.4.1)
1528    # Project name
1529    project(XComponent)
1530
1531    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1532    add_definitions(-DOHOS_PLATFORM)
1533    # Set the header file search directory.
1534    include_directories(
1535        ${NATIVERENDER_ROOT_PATH}
1536        ${NATIVERENDER_ROOT_PATH}/include
1537    )
1538    # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files.
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    # Add the libraries to be linked.
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## Lifecycle Description
1600
1601You can use the **XComponent** to develop EGL/OpenGL ES rendering by using the following ArkTS code:
1602
1603```typescript
1604@Builder
1605function myComponent() {
1606  XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' })
1607    .onLoad((context) => {})
1608    .onDestroy(() => {})
1609}
1610```
1611
1612### **onLoad** event
1613
1614This event is triggered when the surface of the **XComponent** is ready.
1615
1616**context** parameter: where the native API exposed on the module is mounted. Its usage is similar to the usage of a **context** instance obtained after the module is directly loaded using **import context from "libnativerender.so"**.
1617
1618**Sequence**:
1619
1620​	Native XComponent scenario:
1621
1622​	The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the native side is shown in the figure below.
1623
1624![onLoad](./figures/onLoad.png)
1625
1626​	ArkTS XComponent scenario:
1627
1628​	The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the ArkTS side is shown in the figure below.
1629
1630![onLoad](./figures/onLoad1.png)
1631
1632
1633
1634### **onDestroy** event
1635
1636This event is triggered when the **XComponent** component is destroyed, which is the same as the destruction time of common ArkUI components.
1637
1638**Sequence**:
1639
1640​	Native XComponent scenario:
1641
1642​	The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the native side is shown in the figure below.
1643
1644![onDestroy](./figures/onDestroy.png)
1645
1646
1647
1648​	ArkTS XComponent scenario:
1649
1650​	The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the ArkTS side is shown in the figure below.
1651
1652![onDestroy](./figures/onDestroy1.png)
1653
1654