• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 the held [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 three scenarios:
81. Managing the surface lifecycle with **XComponentController**: In this scenario, the **SurfaceId** is obtained on the ArkTS side; lifecycle callbacks and event callbacks (such as touch, mouse, and key event callbacks) are all triggered on the ArkTS side.
92. Managing the surface lifecycle with **OH_ArkUI_SurfaceHolder**: In this scenario, the **OH_ArkUI_SurfaceHolder** instance is created based on the **ArkUI_NodeHandle** corresponding to the **XComponent**; lifecycle callbacks, event callbacks, accessibility and variable frame rate callbacks are all triggered on the native side.
103. Managing the surface lifecycle with **NativeXComponent**: In this scenario, the native **XComponent** instance is obtained at the native layer, and the lifecycle callbacks of **XComponent**, as well as touch, mouse, key and other event callbacks are registered on the native side.
11
12## How Custom Drawing Works
13
14The **XComponent** provides a surface for custom drawing. To draw custom content on this surface, you can use the [NativeWindow](../graphics/native-window-guidelines.md) API to allocate and submit graphics buffers. This process pushes your custom content to the surface, and the **XComponent** then integrates the surface into the UI and displays the result. By default, the surface matches the size and position of the **XComponent**. Yet, you can adjust its position and size using the [setXComponentSurfaceRect](../reference/apis-arkui/arkui-ts/ts-basic-components-xcomponent.md#setxcomponentsurfacerect12) API if needed.
15
16The **XComponent** is responsible for creating the surface and notifying the application of surface-related information through callbacks. Applications can set the properties of the surface through dedicated APIs. It should be note that the component itself has no awareness of the actual drawn content and does not provide direct rendering APIs.
17
18> **NOTE**
19>
20> If your custom drawn content includes transparent elements, they will blend with the content below the surface. For example, if your content is fully transparent, the background of the **XComponent** is black, and the surface maintains its default size and position, the final display will be a black area.
21
22## Managing the Surface Lifecycle with XComponentController
23
24This scenario involves obtaining the **SurfaceId** on the ArkTS side, with layout information, lifecycle callbacks, and event callbacks (such as touch, mouse, and key event callbacks) all triggered on the ArkTS side before being optionally passed to the native side for processing. The development mainly involves the following use cases:
25- Use the **SurfaceId** obtained on the ArkTS side to call the **OH_NativeWindow_CreateNativeWindowFromSurfaceId** API on the native side to create a **NativeWindow** instance.
26- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and allocate and submit buffers to the graphics queue.
27- Obtain lifecycle and event information on the ArkTS side and pass it to the native side for processing.
28
29> **NOTE**
30>
31> 1. **NativeWindow** instances are cached in a dictionary on the native side. Their keys must be globally unique, and they must be promptly removed from the dictionary when the corresponding **XComponent** component is destroyed.
32>
33> 2. For the **XComponent** components of the TEXTURE or SURFACE type created using [typeNode](../reference/apis-arkui/js-apis-arkui-frameNode.md#typenode12), due to their lifecycle differences from declarative components, the buffer size remains unset after component creation. Therefore, before starting to draw content, call the [OH_NativeWindow_NativeWindowHandleOpt](../reference/apis-arkgraphics2d/capi-external-window-h.md#oh_nativewindow_nativewindowhandleopt) API to set the buffer size.
34>
35> 3. During development with multiple **XComponent** components, make sure the keys used to cache resources on the Native side are unique. The two recommended key formats are as follows: **Id** + random number; **surfaceId**.
36
37**lifecycle Callbacks**
38
39- onSurfaceCreated
40
41  Triggered when the surface of the **XComponent** component is ready.
42
43  ArkTS-side sequence
44
45  ![OnSurfaceCreated](./figures/onSurfaceCreated1.png)
46
47- onSurfaceChanged
48
49  Triggered after surface size changes trigger re-layout.
50
51  ArkTS-side sequence
52
53  ![OnSurfaceChanged](./figures/onSurfaceChanged1.png)
54
55- onSurfaceDestroyed
56
57  Triggered when the **XComponent** component is destroyed, which is consistent with the destruction timing of common ArkUI components.
58
59  ArkTS-side sequence
60
61  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed1.png)
62
63**Available APIs**
64
65XComponentController on the ArkTS side
66
67| API                                                      | Description                                                        |
68| ------------------------------------------------------------ | ------------------------------------------------------------ |
69| getXComponentSurfaceId(): string                             | Obtains the ID of the surface associated with the **XComponent**.                               |
70| onSurfaceCreated(surfaceId: string): void                    | Called when the surface held by the **XComponent** is created.                   |
71| 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).|
72| onSurfaceDestroyed(surfaceId: string): void                  | Called when the surface held by the **XComponent** is destroyed.                   |
73
74Native side
75
76| API                                                      | Description                                                        |
77| ------------------------------------------------------------ | ------------------------------------------------------------ |
78| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | Creates an **OHNativeWindow** instance based on a surface ID.                       |
79| 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.|
80
81**How to Develop**
82
83The 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.
84
851. Define the **XComponent** on the UI.
86
87    ```javascript
88    // Function declarations defined in cpp/types/libnativerender/Index.d.ts
89    type XComponentContextStatus = {
90        hasDraw: boolean,
91        hasChangeColor: boolean,
92    };
93    export const SetSurfaceId: (id: BigInt) => any;
94    export const ChangeSurface: (id: BigInt, w: number, h: number) =>any;
95    export const DrawPattern: (id: BigInt) => any;
96    export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus
97    export const ChangeColor: (id: BigInt) => any;
98    export const DestroySurface: (id: BigInt) => any;
99    ```
100
101    ```typescript
102    import nativeRender from 'libnativerender.so';
103
104    // Override XComponentController to set lifecycle callbacks.
105    class MyXComponentController extends XComponentController {
106        onSurfaceCreated(surfaceId: string): void {
107            console.log(`onSurfaceCreated surfaceId: ${surfaceId}`)
108            nativeRender.SetSurfaceId(BigInt(surfaceId));
109        }
110
111        onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void {
112            console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`)
113            // Call ChangeSurface to draw content in onSurfaceChanged.
114            nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight)
115        }
116
117        onSurfaceDestroyed(surfaceId: string): void {
118            console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`)
119            nativeRender.DestroySurface(BigInt(surfaceId))
120        }
121    }
122
123    @Entry
124    @Component
125    struct Index {
126        @State currentStatus: string = "index";
127        xComponentController: XComponentController = new MyXComponentController();
128
129        build() {
130            Column() {
131                //...
132                // Define XComponent in an .ets file.
133                Column({ space: 10 }) {
134                    XComponent({
135                        type: XComponentType.SURFACE,
136                        controller: this.xComponentController
137                    })
138                Text(this.currentStatus)
139                    .fontSize('24fp')
140                    .fontWeight(500)
141                }
142                .onClick(() => {
143                    let surfaceId = this.xComponentController.getXComponentSurfaceId()
144                    nativeRender.ChangeColor(BigInt(surfaceId))
145                    let hasChangeColor: boolean = false;
146                    if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
147                        hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor;
148                    }
149                    if (hasChangeColor) {
150                        this.currentStatus = "change color";
151                    }
152                })
153
154                //...
155                Row() {
156                    Button('Draw Star')
157                        .fontSize('16fp')
158                        .fontWeight(500)
159                        .margin({ bottom: 24 })
160                        .onClick(() => {
161                            let surfaceId = this.xComponentController.getXComponentSurfaceId()
162                            nativeRender.DrawPattern(BigInt(surfaceId))
163                            let hasDraw: boolean = false;
164                            if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) {
165                                hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw;
166                            }
167                            if (hasDraw) {
168                                this.currentStatus = "draw star"
169                            }
170                        })
171                        .width('53.6%')
172                        .height(40)
173                }
174                .width('100%')
175                .justifyContent(FlexAlign.Center)
176                .alignItems(VerticalAlign.Bottom)
177                .layoutWeight(1)
178            }
179            .width('100%')
180            .height('100%')
181        }
182    }
183    ```
184
1852. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md).
186
187    ```typescript
188    #include <hilog/log.h>
189    #include "common/common.h"
190    #include "manager/plugin_manager.h"
191    namespace NativeXComponentSample {
192    // 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.
193    EXTERN_C_START
194    static napi_value Init(napi_env env, napi_value exports) {
195        // ...
196        // Expose the SetSurfaceId(), ChangeSurface(), and DestroySurface() APIs to the ArkTS side.
197        // DrawPattern(),ChangeColor(),GetXComponentStatus()
198        napi_property_descriptor desc[] = {
199            {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
200            {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr},
201            {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr},
202            {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr},
203            {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr},
204            {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default,
205             nullptr}};
206        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
207            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
208            return nullptr;
209        }
210        return exports;
211    }
212    EXTERN_C_END
213    // Provide module descriptor configuration. You can modify parameters as needed.
214    static napi_module nativerenderModule = {.nm_version = 1,
215                                             .nm_flags = 0,
216                                             .nm_filename = nullptr,
217                                             // Entry point function
218                                             .nm_register_func = Init,
219                                             // Module name
220                                             .nm_modname = "nativerender",
221                                             .nm_priv = ((void *)0),
222                                             .reserved = {0}};
223    } // namespace NativeXComponentSample
224    // 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 descriptor configuration for module registration.
225    extern "C" __attribute__((constructor)) void RegisterModule(void) {
226        napi_module_register(&NativeXComponentSample::nativerenderModule);
227    }
228    ```
229
2303. Implement the preceding six registered functions on the native side.
231
232    ```cpp
233    // Define the PluginManager class.
234    class PluginManager {
235    public:
236        ~PluginManager();
237        static PluginRender *GetPluginRender(int64_t &id);
238        static napi_value ChangeColor(napi_env env, napi_callback_info info);
239        static napi_value DrawPattern(napi_env env, napi_callback_info info);
240        static napi_value SetSurfaceId(napi_env env, napi_callback_info info);
241        static napi_value ChangeSurface(napi_env env, napi_callback_info info);
242        static napi_value DestroySurface(napi_env env, napi_callback_info info);
243        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
244
245    public:
246        static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_;
247        static std::unordered_map<int64_t, OHNativeWindow *> windowMap_;
248    };
249
250    // Parse the surfaceId passed from ArkTS. Here, surfaceId is a 64-bit integer value.
251    int64_t ParseId(napi_env env, napi_callback_info info) {
252        if ((env == nullptr) || (info == nullptr)) {
253            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null");
254            return -1;
255        }
256        size_t argc = 1;
257        napi_value args[1] = {nullptr};
258        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
259            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed");
260            return -1;
261        }
262        int64_t value = 0;
263        bool lossless = true;
264        if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) {
265            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed");
266            return -1;
267        }
268        return value;
269    }
270
271    // Set SurfaceId and initialize NativeWindow based on SurfaceId.
272    napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) {
273        int64_t surfaceId = ParseId(env, info);
274        OHNativeWindow *nativeWindow;
275        PluginRender *pluginRender;
276        if (windowMap_.find(surfaceId) == windowMap_.end()) {
277            OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow);
278            windowMap_[surfaceId] = nativeWindow;
279        }
280        if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) {
281            pluginRender = new PluginRender(surfaceId);
282            pluginRenderMap_[surfaceId] = pluginRender;
283        }
284        pluginRender->InitNativeWindow(nativeWindow);
285        return nullptr;
286    }
287
288    void PluginRender::InitNativeWindow(OHNativeWindow *window) {
289        eglCore_->EglContextInit(window); // For details about the EglContextInit implementation, see the "Native XComponent Scenario" section.
290    }
291
292    // Implement surface size changes based on the passed surfaceId, width, and height.
293    napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) {
294        if ((env == nullptr) || (info == nullptr)) {
295            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
296                         "ChangeSurface: OnLoad env or info is null");
297            return nullptr;
298        }
299        int64_t surfaceId = 0;
300        size_t argc = 3;
301        napi_value args[3] = {nullptr};
302
303        if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) {
304            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
305                         "ChangeSurface: GetContext napi_get_cb_info failed");
306            return nullptr;
307        }
308        bool lossless = true;
309        int index = 0;
310        if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) {
311            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed");
312            return nullptr;
313        }
314        double width;
315        if (napi_ok != napi_get_value_double(env, args[index++], &width)) {
316            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed");
317            return nullptr;
318        }
319        double height;
320        if (napi_ok != napi_get_value_double(env, args[index++], &height)) {
321            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed");
322            return nullptr;
323        }
324        auto pluginRender = GetPluginRender(surfaceId);
325        if (pluginRender == nullptr) {
326            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed");
327            return nullptr;
328        }
329        pluginRender->UpdateNativeWindowSize(width, height);
330        return nullptr;
331    }
332
333    void PluginRender::UpdateNativeWindowSize(int width, int height) {
334        eglCore_->UpdateSize(width, height); // For details about the UpdateSize implementation, see the "Native XComponent Scenario" section.
335        if (!hasChangeColor_ && !hasDraw_) {
336            eglCore_->Background(); // For details about the Background implementation, see the "Native XComponent Scenario" section.
337        }
338    }
339
340    // Destroy the surface.
341    napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) {
342        int64_t surfaceId = ParseId(env, info);
343        auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId);
344        if (pluginRenderMapIter != pluginRenderMap_.end()) {
345            delete pluginRenderMapIter->second;
346            pluginRenderMap_.erase(pluginRenderMapIter);
347        }
348        auto windowMapIter = windowMap_.find(surfaceId);
349        if (windowMapIter != windowMap_.end()) {
350            OH_NativeWindow_DestroyNativeWindow(windowMapIter->second);
351            windowMap_.erase(windowMapIter);
352        }
353        return nullptr;
354    }
355
356    // Implement the EGL drawing logic.
357    napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) {
358        int64_t surfaceId = ParseId(env, info);
359        auto pluginRender = GetPluginRender(surfaceId);
360        if (pluginRender == nullptr) {
361            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed");
362            return nullptr;
363        }
364        pluginRender->DrawPattern();
365        return nullptr;
366    }
367
368    PluginRender *PluginManager::GetPluginRender(int64_t &id) {
369        if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) {
370            return pluginRenderMap_[id];
371        }
372        return nullptr;
373    }
374
375    void PluginRender::DrawPattern() {
376        eglCore_->Draw(hasDraw_); // For details about the Draw implementation, see the "Native XComponent Scenario" section.
377    }
378
379    // Implement the feature of changing the color of the drawn graphics.
380    napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) {
381        int64_t surfaceId = ParseId(env, info);
382        auto pluginRender = GetPluginRender(surfaceId);
383        if (pluginRender == nullptr) {
384            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed");
385            return nullptr;
386        }
387        pluginRender->ChangeColor(); // For details about the ChangeColor implementation, see the "Native XComponent Scenario" section.
388        return nullptr;
389    }
390
391    void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); }
392
393    // Obtain the XComponent status and return it to the ArkTS side.
394    napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) {
395        int64_t surfaceId = ParseId(env, info);
396        auto pluginRender = GetPluginRender(surfaceId);
397        if (pluginRender == nullptr) {
398            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
399                         "GetXComponentStatus: Get pluginRender failed");
400            return nullptr;
401        }
402        napi_value hasDraw;
403        napi_value hasChangeColor;
404        napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw));
405        if (ret != napi_ok) {
406            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
407                         "GetXComponentStatus: napi_create_int32 hasDraw_ error");
408            return nullptr;
409        }
410        ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor));
411        if (ret != napi_ok) {
412            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
413                         "GetXComponentStatus: napi_create_int32 hasChangeColor_ error");
414            return nullptr;
415        }
416        napi_value obj;
417        ret = napi_create_object(env, &obj);
418        if (ret != napi_ok) {
419            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
420                         "GetXComponentStatus: napi_create_object error");
421            return nullptr;
422        }
423        ret = napi_set_named_property(env, obj, "hasDraw", hasDraw);
424        if (ret != napi_ok) {
425            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
426                         "GetXComponentStatus: napi_set_named_property hasDraw error");
427            return nullptr;
428        }
429        ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor);
430        if (ret != napi_ok) {
431            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
432                         "GetXComponentStatus: napi_set_named_property hasChangeColor error");
433            return nullptr;
434        }
435        return obj;
436    }
437
438    int32_t PluginRender::HasDraw() { return hasDraw_; }
439
440    int32_t PluginRender::HasChangedColor() { return hasChangeColor_; }
441    ```
442
4434. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file.
444
445    ```cmake
446    # Set the minimum CMake version.
447    cmake_minimum_required(VERSION 3.4.1)
448    # Project name
449    project(XComponent)
450
451    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
452    add_definitions(-DOHOS_PLATFORM)
453    # Set the header file search directory.
454    include_directories(
455        ${NATIVERENDER_ROOT_PATH}
456        ${NATIVERENDER_ROOT_PATH}/include
457    )
458    # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files.
459    add_library(nativerender SHARED
460        render/egl_core.cpp
461        render/plugin_render.cpp
462        manager/plugin_manager.cpp
463        napi_init.cpp
464    )
465
466    find_library(
467        # Set the name of the path variable.
468        EGL-lib
469        # Set the name of the NDK library to search for.
470        EGL
471    )
472
473    find_library(
474        # Set the name of the path variable.
475        GLES-lib
476        # Set the name of the NDK library to search for.
477        GLESv3
478    )
479
480    find_library(
481        # Set the name of the path variable.
482        hilog-lib
483        # Set the name of the NDK library to search for.
484        hilog_ndk.z
485    )
486
487    find_library(
488        # Set the name of the path variable.
489        libace-lib
490        # Set the name of the NDK library to search for.
491        ace_ndk.z
492    )
493
494    find_library(
495        # Set the name of the path variable.
496        libnapi-lib
497        # Set the name of the NDK library to search for.
498        ace_napi.z
499    )
500
501    find_library(
502        # Set the name of the path variable.
503        libuv-lib
504        # Set the name of the NDK library to search for.
505        uv
506    )
507    # Add the libraries to be linked.
508    target_link_libraries(nativerender PUBLIC
509        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
510    ```
511
512## Managing the Surface Lifecycle with OH_ArkUI_SurfaceHolder
513
514Unlike the scenario where the surface lifecycle is managed with **XComponentController**, this scenario allows applications to create an **OH_ArkUI_SurfaceHolder** object based on the **ArkUI_NodeHandle** corresponding to the **XComponent** component. Through relevant APIs on the **OH_ArkUI_SurfaceHolder**, you can register surface lifecycle callbacks and implement **XComponent** capabilities such as accessibility and variable frame rate. In addition, listening for basic and gesture events on the **XComponent** component can be achieved using ArkUI NDK APIs through the **ArkUI_NodeHandle** object. For details, see [Listening for Component Events](./ndk-listen-to-component-events.md). The development mainly involves the following use cases:
515- For **XComponent** components created on the ArkTS side, you can pass the corresponding FrameNode to the native side to obtain an **ArkUI_NodeHandle** object. For **XComponent** components created on the native side, you can directly obtain the **ArkUI_NodeHandle** object. Then, call the **OH_ArkUI_SurfaceHolder_Create** API to create an **OH_ArkUI_SurfaceHolder** instance.
516- Register lifecycle and event callbacks using the **OH_ArkUI_SurfaceHolder** instance and obtain a **NativeWindow** instance.
517- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and allocate and submit buffers to the graphics queue.
518
519**lifecycle Callbacks**
520
521- OnSurfaceCreated
522
523  Triggered when the surface of the XComponent component is ready and either of the following conditions is met:
524  1. The component is attached to the component tree with **autoInitialize = true**.
525  2. The **OH_ArkUI_XComponent_Initialize** API is called.
526
527  ArkTS-side sequence
528
529  ![OnSurfaceCreated](./figures/onSurfaceCreated2.png)
530- OnSurfaceChanged
531
532  Triggered when surface size changes trigger re-layout, after the **OnSurfaceCreated** callback is successfully triggered.
533
534  ArkTS-side sequence
535
536  ![OnSurfaceChanged](./figures/onSurfaceChanged2.png)
537
538- OnSurfaceDestroyed
539
540  Triggered when the component is detached from the component tree with **autoInitialize=true** or the **OH_ArkUI_XComponent_Finalize** API is called.
541
542  ArkTS-side sequence
543
544  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed2.png)
545
546
547**Available APIs**
548
549| API                                                      | Description                                                        |
550| ------------------------------------------------------------ | ------------------------------------------------------------ |
551| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | Obtains the native API set of a specified type.                                        |
552| OH_ArkUI_XComponent_GetNativeWindow(OH_ArkUI_SurfaceHolder* surfaceHolder) | Obtains the **NativeWindow** instance associated with an **OH_ArkUI_SurfaceHolder** instance.                                        |
553| OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | Removes the previously added surface lifecycle callback from an **OH_ArkUI_SurfaceHolder** instance.                                        |
554| OH_ArkUI_SurfaceCallback_Dispose(OH_ArkUI_SurfaceCallback* callback) | Disposes of an **OH_ArkUI_SurfaceCallback** object.                                        |
555| OH_ArkUI_SurfaceHolder_Dispose(OH_ArkUI_SurfaceHolder* surfaceHolder) | Disposes of an **OH_ArkUI_SurfaceHolder** object.                                        |
556| OH_ArkUI_NodeEvent_GetEventType(ArkUI_NodeEvent* event) | Obtains the event type from a component event.                                        |
557| OH_ArkUI_NodeEvent_GetNodeHandle(ArkUI_NodeEvent* event) | Obtains the component object that triggers a component event.                                        |
558| OH_ArkUI_GetNodeHandleFromNapiValue(napi_env env, napi_value frameNode, ArkUI_NodeHandle* handle) | Obtains an **ArkUI_NodeHandle** object on the native side mapped from the **FrameNode** object created on the ArkTS side.                                        |
559| OH_ArkUI_SurfaceHolder_Create(ArkUI_NodeHandle node) | Creates an **OH_ArkUI_SurfaceHolder** object from the **XComponent** node.                                      |
560| OH_ArkUI_SurfaceCallback_Create() | Creates an **OH_ArkUI_SurfaceCallback** object.                                        |
561| OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceCreated)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | Registers the **onSurfaceCreated** callback with an **OH_ArkUI_SurfaceCallback** object.                                        |
562| OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceChanged)(OH_ArkUI_SurfaceHolder* surfaceHolder, uint64_t width, uint64_t height)) | Registers the **onSurfaceChanged** callback with an **OH_ArkUI_SurfaceCallback** object.                                        |
563| OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceDestroyed)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | Registers the **onSurfaceDestroyed** callback with an **OH_ArkUI_SurfaceCallback** object.                                        |
564| OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceShow)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | Registers the **onSurfaceShow** callback with an **OH_ArkUI_SurfaceCallback** object.                                        |
565| OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(OH_ArkUI_SurfaceCallback* callback, void (\*onSurfaceHide)(OH_ArkUI_SurfaceHolder* surfaceHolder)) | Registers the **onSurfaceHide** callback with an **OH_ArkUI_SurfaceCallback** object.                                        |
566| OH_ArkUI_XComponent_RegisterOnFrameCallback(ArkUI_NodeHandle node, void (*callback)(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp)) | Registers the **onFrame** callback for the **XComponent** node.                                        |
567| OH_ArkUI_SurfaceHolder_AddSurfaceCallback(OH_ArkUI_SurfaceHolder* surfaceHolder, OH_ArkUI_SurfaceCallback* callback) | Registers the **OH_ArkUI_SurfaceCallback** object with an **OH_ArkUI_SurfaceHolder** instance.                                        |
568| OH_ArkUI_AccessibilityProvider_Create(ArkUI_NodeHandle node) | Creates an **ArkUI_AccessibilityProvider** object from the **XComponent** node.                                        |
569| OH_ArkUI_XComponent_UnregisterOnFrameCallback(ArkUI_NodeHandle node) | Unregisters the **onFrame callback** of the **XComponent** node.                                        |
570| OH_ArkUI_AccessibilityProvider_Dispose(ArkUI_AccessibilityProvider* provider) | Disposes of an **ArkUI_AccessibilityProvider** object.                                       |
571| OH_ArkUI_XComponent_SetExpectedFrameRateRange(ArkUI_NodeHandle node, OH_NativeXComponent_ExpectedRateRange range) | Sets the expected frame rate range for the **XComponent** node.                                        |
572| OH_ArkUI_XComponent_SetNeedSoftKeyboard(ArkUI_NodeHandle node, bool needSoftKeyboard) | Sets whether the **XComponent** node needs to display the soft keyboard when it gains focus.                                        |
573
574**How to Develop**
575
576This example shows how to create an **XComponent** of the SURFACE type on the ArkTS side (for details about how to create an **ArkUI_NodeHandle** object corresponding to the **XComponent** on the native side, see [ArkUI_NativeNodeAPI_1](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md)). It describes how to use the **XComponent** component to call APIs related to **OH_ArkUI_SurfaceHolder** for managing the surface lifecycle, create an EGL/OpenGL ES environment on the native side to draw graphics on the main page, and change the color of the graphics.
577
5781. Define the **XComponent** on the UI.
579
580    ```typescript
581    import native from 'libnativerender.so';
582
583    @Entry
584    @Component
585    struct Index {
586        xcomponentId: string = 'xcp' + (new Date().getTime());
587        @State isShow: boolean = true;
588        @State minRate: number = 0;
589        @State maxRate: number = 120;
590        @State expected: number = 60;
591        needSoftKeyboard: boolean = false;
592        @State needSoftKeyboardState: string = 'needSoftKeyboard=' + this.needSoftKeyboard;
593        @State text: string = 'Tap XComponent to hide the soft keyboard'
594        controller: TextInputController = new TextInputController()
595
596        build() {
597            Column() {
598                TextInput({ text: this.text, placeholder: 'please input ...', controller: this.controller })
599                    .placeholderColor(Color.Grey)
600                    .placeholderFont({ size: 14, weight: 400 })
601                    .caretColor(Color.Blue)
602                    .width(400)
603                    .height(40)
604                    .margin(20)
605                    .fontSize(14)
606                    .fontColor(Color.Black)
607                    .onChange((value: string) => {
608                        this.text = value
609                    })
610                Column() {
611                    if (this.isShow) {
612                        XComponent({
613                            type: XComponentType.SURFACE
614                        })
615                            .id(this.xcomponentId)
616                            .onAttach(() => {
617                                let node = this.getUIContext().getFrameNodeById(this.xcomponentId)
618                                native.bindNode(this.xcomponentId, node)
619                            })
620                            .onDetach(() => {
621                                native.unbindNode(this.xcomponentId)
622                            })
623                            .width(200)
624                            .height(200)
625                            .focusable(true)
626                            .focusOnTouch(true)
627                            .defaultFocus(true)
628                    }
629                }.height(200)
630
631                Button('Create/Destroy').onClick(() => {
632                    this.isShow = !this.isShow;
633                })
634
635                Column() {
636                    Text('Expected frame rate')
637                        .textAlign(TextAlign.Start)
638                        .fontSize(15)
639                        .border({ width: 1 })
640                        .padding(10)
641                        .width('100%')
642                        .margin(5)
643                    Text('min: ' + this.minRate)
644                    Slider({
645                        value: this.minRate,
646                        min: 0,
647                        max: 240,
648                        step: 1
649                    }).onChange((value: number, mode: SliderChangeMode) => {
650                        this.minRate = value;
651                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
652                    }).width('100%')
653                    Text('max: ' + this.maxRate)
654                    Slider({
655                        value: this.maxRate,
656                        min: 0,
657                        max: 240,
658                        step: 1
659                    }).onChange((value: number, mode: SliderChangeMode) => {
660                        this.maxRate = value;
661                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
662                    }).width('100%')
663                    Text('expected: ' + this.expected)
664                    Slider({
665                        value: this.expected,
666                        min: 0,
667                        max: 240,
668                        step: 1
669                    }).onChange((value: number, mode: SliderChangeMode) => {
670                        this.expected = value;
671                        native.setFrameRate(this.xcomponentId, this.minRate, this.maxRate, this.expected)
672                    }).width('100%')
673                }.backgroundColor("#F0FAFF")
674
675                Button(this.needSoftKeyboardState)
676                    .onClick(() => {
677                        this.needSoftKeyboard = !this.needSoftKeyboard;
678                        this.needSoftKeyboardState = 'needSoftKeyboard=' + this.needSoftKeyboard;
679                        native.setNeedSoftKeyboard(this.xcomponentId, this.needSoftKeyboard);
680                        this.text = this.needSoftKeyboard ? 'Tapping XComponent will not hide the keyboard' : 'Tap XComponent to hide the keyboard'
681                    })
682            }
683            .width('100%')
684        }
685    }
686    ```
687
6882. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md).
689
690    ```c++
691    #include "manager/plugin_manager.h"
692
693    namespace NativeXComponentSample {
694
695    // 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.
696    EXTERN_C_START
697    static napi_value Init(napi_env env, napi_value exports)
698    {
699        // Expose APIs to the ArkTS side.
700        napi_property_descriptor desc[] = {
701            {"bindNode", nullptr, PluginManager::BindNode,
702                nullptr, nullptr, nullptr, napi_default, nullptr},
703            {"unbindNode", nullptr, PluginManager::UnbindNode,
704                nullptr, nullptr, nullptr, napi_default, nullptr},
705            {"setFrameRate", nullptr, PluginManager::SetFrameRate,
706                nullptr, nullptr, nullptr, napi_default, nullptr},
707            {"setNeedSoftKeyboard", nullptr, PluginManager::SetNeedSoftKeyboard,
708                nullptr, nullptr, nullptr, napi_default, nullptr},
709        };
710        napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
711        return exports;
712    }
713    EXTERN_C_END
714
715    // Provide module descriptor configuration. You can modify parameters as needed.
716    static napi_module demoModule = {
717        .nm_version = 1,
718        .nm_flags = 0,
719        .nm_filename = nullptr,
720        // Entry point function
721        .nm_register_func = Init, // Specify the callback for when the corresponding module is loaded.
722        // Module name
723        .nm_modname = "nativerender", // Specify the module name, which must be consistent with the value of libraryname in the XComponent declaration on the ArkTS side.
724        .nm_priv = ((void*)0),
725        .reserved = { 0 },
726    };
727    }
728
729    // 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 descriptor configuration for module registration.
730    extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
731    {
732        napi_module_register(&NativeXComponentSample::demoModule);
733    }
734    ```
735
7363. Register the XComponent lifecycle, event, accessibility, and variable frame rate callbacks using C APIs.
737
738    (1) Define the **BindNode**, **UnbindNode**, **SetFrameRate**, and **SetNeedSoftKeyboard** APIs, which will be executed by the corresponding **bindNode**, **unbindNode**, **setFrameRate**, and **setNeedSoftKeyboard** APIs exposed to the ArkTS side.
739
740    ```c++
741    // plugin_manager.h
742    namespace NativeXComponentSample {
743    class PluginManager {
744    public:
745        ~PluginManager();
746        static napi_value BindNode(napi_env env, napi_callback_info info);
747        static napi_value UnbindNode(napi_env env, napi_callback_info info);
748        static napi_value SetFrameRate(napi_env env, napi_callback_info info);
749        static napi_value SetNeedSoftKeyboard(napi_env env, napi_callback_info info);
750
751    public:
752        static std::unordered_map<std::string, ArkUI_NodeHandle> nodeHandleMap_;
753        static std::unordered_map<void *, EGLRender *> renderMap_;
754        static std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> callbackMap_;
755        static std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> surfaceHolderMap_;
756        static ArkUI_AccessibilityProvider* provider_;
757    };
758    }
759    ```
760
761    ```c++
762    // plugin_manager.cpp
763    std::unordered_map<std::string, ArkUI_NodeHandle> PluginManager::nodeHandleMap_;
764    std::unordered_map<void *, EGLRender *> PluginManager::renderMap_;
765    std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> PluginManager::callbackMap_;
766    std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> PluginManager::surfaceHolderMap_;
767    ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
768        OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
769
770    std::string value2String (napi_env env, napi_value value) { // Convert napi_value to a string variable.
771        size_t stringSize = 0;
772        napi_get_value_string_utf8(env, value, nullptr, 0, &stringSize);
773        std::string valueString;
774        valueString.resize(stringSize);
775        napi_get_value_string_utf8(env, value, &valueString[0], stringSize + 1, &stringSize);
776        return valueString;
777    }
778
779    napi_value PluginManager::BindNode(napi_env env, napi_callback_info info) {
780        size_t argc = 2;
781        napi_value args[2] = {nullptr};
782        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
783        std::string nodeId = value2String(env, args[0]);
784        ArkUI_NodeHandle handle;
785        OH_ArkUI_GetNodeHandleFromNapiValue(env, args[1], &handle); // Obtain the nodeHandle object.
786        OH_ArkUI_SurfaceHolder *holder = OH_ArkUI_SurfaceHolder_Create (handle); // Obtain a SurfaceHolder object.
787        nodeHandleMap_[nodeId] = handle;
788        surfaceHolderMap_[handle] = holder;
789        auto callback = OH_ArkUI_SurfaceCallback_Create(); // Create a SurfaceCallback object.
790        callbackMap_[holder] = callback;
791        OH_ArkUI_SurfaceCallback_SetSurfaceCreatedEvent(callback, OnSurfaceCreated); // Register the OnSurfaceCreated callback.
792        OH_ArkUI_SurfaceCallback_SetSurfaceChangedEvent(callback, OnSurfaceChanged); // Register the OnSurfaceChanged callback.
793        OH_ArkUI_SurfaceCallback_SetSurfaceDestroyedEvent(callback, OnSurfaceDestroyed); // Register the OnSurfaceDestroyed callback.
794        OH_ArkUI_SurfaceCallback_SetSurfaceShowEvent(callback, OnSurfaceShow); // Register the OnSurfaceShow callback.
795        OH_ArkUI_SurfaceCallback_SetSurfaceHideEvent(callback, OnSurfaceHide); // Register the OnSurfaceHide callback.
796        OH_ArkUI_XComponent_RegisterOnFrameCallback(handle, OnFrameCallback); // Register the OnFrameCallback callback.
797        OH_ArkUI_SurfaceHolder_AddSurfaceCallback(holder, callback); // Register the SurfaceCallback callback.
798        if (!nodeAPI->addNodeEventReceiver(handle, onEvent)) { // Add an event listener. The return code 0 indicates success.
799            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "addNodeEventReceiver error");
800        }
801        if (!nodeAPI->registerNodeEvent(handle, NODE_TOUCH_EVENT, 0, nullptr)) { // Register the touch event using the C API. The return code 0 indicates success.
802            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "registerTouchEvent error");
803        }
804        provider_ = OH_ArkUI_AccessibilityProvider_Create(handle); // Create an object of the ArkUI_AccessibilityProvider type.
805        /**
806        * After obtaining the ArkUI_AccessibilityProvider object, you can register accessibility callbacks. For details, see:
807        * https://gitee.com/openharmony/docs/blob/OpenHarmony-5.1.0-Release/en/application-dev/ui/ndk-accessibility-xcomponent.md
808        * **/
809        return nullptr;
810    }
811
812    napi_value PluginManager::UnbindNode(napi_env env, napi_callback_info info)
813    {
814        size_t argc = 1;
815        napi_value args[1] = {nullptr};
816        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
817        std::string nodeId = value2String(env, args[0]);
818        auto node = nodeHandleMap_[nodeId];
819        OH_ArkUI_XComponent_UnregisterOnFrameCallback(node); // Unregister the frame callback.
820        OH_ArkUI_AccessibilityProvider_Dispose(provider_); // Dispose of the ArkUI_AccessibilityProvider object.
821        nodeAPI->disposeNode (node); // Destroy the nodeHandle object.
822        nodeHandleMap_.erase(nodeId);
823        return nullptr;
824    }
825
826    napi_value PluginManager::SetFrameRate(napi_env env, napi_callback_info info)
827    {
828        size_t argc = 4;
829        napi_value args[4] = {nullptr};
830        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
831        std::string nodeId = value2String(env, args[0]);
832        auto node = nodeHandleMap_[nodeId];
833
834        int32_t min = 0;
835        napi_get_value_int32( env, args[1], &min);
836
837        int32_t max = 0;
838        napi_get_value_int32(env, args[2], &max);
839
840        int32_t expected = 0;
841        napi_get_value_int32(env, args[3], &expected);
842        OH_NativeXComponent_ExpectedRateRange range = {
843            .min = min,
844            .max = max,
845            .expected = expected
846        };
847        OH_ArkUI_XComponent_SetExpectedFrameRateRange(node, range); // Set the expected frame rate range.
848        return nullptr;
849    }
850
851    napi_value PluginManager::SetNeedSoftKeyboard(napi_env env, napi_callback_info info)
852    {
853        size_t argc = 2;
854        napi_value args[2] = {nullptr};
855        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
856        std::string nodeId = value2String(env, args[0]);
857        auto node = nodeHandleMap_[nodeId];
858
859        bool needSoftKeyboard = false;
860        napi_get_value_bool( env, args[1], &needSoftKeyboard);
861        OH_ArkUI_XComponent_SetNeedSoftKeyboard(node, needSoftKeyboard); // Set whether the soft keyboard is required.
862        return nullptr;
863    }
864    ```
865
866    (2) Define callbacks for surface creation, changes, destruction, and events, as well as variable frame rate callbacks.
867
868    ```c++
869    void OnSurfaceCreated(OH_ArkUI_SurfaceHolder *holder) {
870        auto window = OH_ArkUI_XComponent_GetNativeWindow(holder); // Obtain the **NativeWindow** instance.
871        auto render = new EGLRender();
872        PluginManager::renderMap_[holder] = render;
873        render->SetUpEGLContext(window);
874    }
875
876    void OnSurfaceChanged(OH_ArkUI_SurfaceHolder *holder, uint64_t width, uint64_t height) {
877        if (PluginManager::renderMap_.count(holder)) {
878            auto render = PluginManager::renderMap_[holder];
879            render->SetEGLWindowSize(width, height); // Set the size of the drawing area.
880            render->DrawStar(true); // Draw a five-pointed star.
881        }
882    }
883
884    void OnSurfaceDestroyed(OH_ArkUI_SurfaceHolder *holder) {
885        OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "onBind", "on destroyed");
886        if (PluginManager::renderMap_.count(holder)) { // Destroy the render object.
887            auto render = PluginManager::renderMap_[holder];
888            delete render;
889            PluginManager::renderMap_.erase(holder);
890        }
891        if (PluginManager::callbackMap_.count(holder)) {
892            auto callback = PluginManager::callbackMap_[holder];
893            OH_ArkUI_SurfaceHolder_RemoveSurfaceCallback(holder, callback); // Remove the SurfaceCallback object.
894            OH_ArkUI_SurfaceCallback_Dispose(callback); // Dispose of the surfaceCallback object.
895            PluginManager::callbackMap_.erase(holder);
896        }
897        OH_ArkUI_SurfaceHolder_Dispose(holder); // Dispose of the surfaceHolder object.
898    }
899
900    void OnSurfaceShow(OH_ArkUI_SurfaceHolder* holder)
901    {
902        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface show");
903    }
904
905    void OnSurfaceHide(OH_ArkUI_SurfaceHolder* holder)
906    {
907        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface hide");
908    }
909
910    void OnFrameCallback(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp)
911    {
912        if (!PluginManager::surfaceHolderMap_.count(node)) {
913            return;
914        }
915        static uint64_t count = 0;
916        count++;
917        if (count % 50 == 0) {
918            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "OnFrameCallback count = %{public}ld", count);
919        }
920    }
921
922    void onEvent(ArkUI_NodeEvent *event) {
923        auto eventType = OH_ArkUI_NodeEvent_GetEventType(event); // Obtain the component event type.
924        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on event");
925        if (eventType == NODE_TOUCH_EVENT) {
926            ArkUI_NodeHandle handle = OH_ArkUI_NodeEvent_GetNodeHandle(event); // Obtain the component that triggered the event.
927            auto holder = PluginManager::surfaceHolderMap_[handle];
928            if (PluginManager::renderMap_.count(holder)) {
929                auto render = PluginManager::renderMap_[holder];
930                render->DrawStar(false); // Draw a five-pointed star.
931            }
932            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on touch");
933        }
934    }
935    ```
936
9374. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering surface, and creating and associating the context.
938
939    ```c++
940    // EGLConst.h
941    #include <EGL/egl.h>
942    #include <EGL/eglext.h>
943    #include <GLES3/gl3.h>
944
945    const unsigned int LOG_PRINT_DOMAIN = 0xFF00;
946
947    /**
948    * Program error.
949    */
950    const GLuint PROGRAM_ERROR = 0;
951
952    /**
953    * Position error.
954    */
955    const GLint POSITION_ERROR = -1;
956
957    /**
958    * Default x coordinate.
959    */
960    const int DEFAULT_X_POSITION = 0;
961
962    /**
963    * Default y coordinate.
964    */
965    const int DEFAULT_Y_POSITION = 0;
966
967    /**
968    * Default GL red value.
969    */
970    const GLfloat GL_RED_DEFAULT = 0.0;
971
972    /**
973    * Default GL green value.
974    */
975    const GLfloat GL_GREEN_DEFAULT = 0.0;
976
977    /**
978    * Default GL blue value.
979    */
980    const GLfloat GL_BLUE_DEFAULT = 0.0;
981
982    /**
983    * GL alpha value.
984    */
985    const GLfloat GL_ALPHA_DEFAULT = 1.0;
986
987    /**
988    * Pointer count.
989    */
990    const GLint POINTER_SIZE = 2;
991
992    /**
993    * Triangle fan size.
994    */
995    const GLsizei TRIANGLE_FAN_SIZE = 4;
996
997    /**
998    * 50%.
999    */
1000    const float FIFTY_PERCENT = 0.5;
1001
1002    /**
1003    * Position handle name.
1004    */
1005    const char POSITION_NAME[] = "a_position";
1006
1007    /**
1008    * Background color #f4f4f4.
1009    */
1010    const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f};
1011
1012    /**
1013    * Draw color #7E8FFB.
1014    */
1015    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
1016
1017    /**
1018    * Change color #92D6CC.
1019    */
1020    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
1021
1022    /**
1023    * Background area.
1024    */
1025    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {-1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f};
1026
1027    const EGLint ATTRIB_LIST[] = {
1028        // Key, value.
1029        EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
1030        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1031        // End.
1032        EGL_NONE};
1033
1034    const EGLint CONTEXT_ATTRIBS[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
1035
1036    /**
1037    * Vertex shader.
1038    */
1039    const char VERTEX_SHADER[] = "#version 300 es\n"
1040                                "layout(location = 0) in vec4 a_position;\n"
1041                                "layout(location = 1) in vec4 a_color;   \n"
1042                                "out vec4 v_color;                       \n"
1043                                "void main()                             \n"
1044                                "{                                       \n"
1045                                "   gl_Position = a_position;            \n"
1046                                "   v_color = a_color;                   \n"
1047                                "}                                       \n";
1048
1049    /**
1050    * Fragment shader.
1051    */
1052    const char FRAGMENT_SHADER[] = "#version 300 es\n"
1053                                "precision mediump float;                  \n"
1054                                "in vec4 v_color;                          \n"
1055                                "out vec4 fragColor;                       \n"
1056                                "void main()                               \n"
1057                                "{                                         \n"
1058                                "   fragColor = v_color;                   \n"
1059                                "}                                         \n";
1060    ```
1061
1062    ```c++
1063    // EGLRender.h
1064    #include "EGLConst.h"
1065    #include <EGL/egl.h>
1066    #include <EGL/eglext.h>
1067    #include <EGL/eglplatform.h>
1068    #include <GLES3/gl3.h>
1069    #include <string>
1070
1071    class EGLRender {
1072    public:
1073        bool SetUpEGLContext(void *window);
1074        void SetEGLWindowSize(int width, int height);
1075        void DrawStar(bool drawColor);
1076
1077        std::string xcomponentId;
1078        EGLNativeWindowType eglWindow_;
1079
1080        EGLDisplay eglDisplay_ = EGL_NO_DISPLAY;
1081        EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR;
1082        EGLSurface eglSurface_ = EGL_NO_SURFACE;
1083        EGLContext eglContext_ = EGL_NO_CONTEXT;
1084        GLuint program_;
1085        int width_ = 0;
1086        int height_ = 0;
1087        ~EGLRender();
1088
1089    private:
1090        GLint PrepareDraw();
1091        bool ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]);
1092    };
1093    ```
1094
1095    ```c++
1096    // EGLRender.cpp
1097    #include "EGLRender.h"
1098    #include "EGLConst.h"
1099    #include <EGL/egl.h>
1100    #include <EGL/eglext.h>
1101    #include <GLES3/gl3.h>
1102    #include <cmath>
1103    #include <cstdio>
1104    #include <algorithm>
1105    #include <hilog/log.h>
1106    #include <iostream>
1107
1108    namespace {
1109    void Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat *rotateX, GLfloat *rotateY, GLfloat theta) {
1110        GLfloat tempX = cos(theta) * (*rotateX - centerX) - sin(theta) * (*rotateY - centerY);
1111        GLfloat tempY = sin(theta) * (*rotateX - centerX) + cos(theta) * (*rotateY - centerY);
1112        *rotateX = tempX + centerX;
1113        *rotateY = tempY + centerY;
1114    }
1115
1116    GLuint LoadShader(GLenum type, const char *shaderSrc) {
1117        if ((type <= 0) || (shaderSrc == nullptr)) {
1118            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader type or shaderSrc error");
1119            return PROGRAM_ERROR;
1120        }
1121
1122        GLuint shader = glCreateShader(type);
1123        if (shader == 0) {
1124            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCreateShader unable to load shader");
1125            return PROGRAM_ERROR;
1126        }
1127
1128        // The gl function has no return value.
1129        glShaderSource(shader, 1, &shaderSrc, nullptr);
1130        glCompileShader(shader);
1131
1132        GLint compiled;
1133        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
1134        if (compiled != 0) {
1135            return shader;
1136        }
1137
1138        GLint infoLen = 0;
1139        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
1140        if (infoLen <= 1) {
1141            glDeleteShader(shader);
1142            return PROGRAM_ERROR;
1143        }
1144
1145        char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1));
1146        if (infoLog != nullptr) {
1147            memset(infoLog, 0, infoLen + 1);
1148            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
1149            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glCompileShader error = %s", infoLog);
1150            free(infoLog);
1151            infoLog = nullptr;
1152        }
1153        glDeleteShader(shader);
1154        return PROGRAM_ERROR;
1155    }
1156
1157    // Create a program.
1158    GLuint CreateProgram(const char *vertexShader, const char *fragShader) {
1159        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
1160            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1161                        "createProgram: vertexShader or fragShader is null");
1162            return PROGRAM_ERROR;
1163        }
1164
1165        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
1166        if (vertex == PROGRAM_ERROR) {
1167            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram vertex error");
1168            return PROGRAM_ERROR;
1169        }
1170
1171        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
1172        if (fragment == PROGRAM_ERROR) {
1173            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram fragment error");
1174            return PROGRAM_ERROR;
1175        }
1176
1177        GLuint program = glCreateProgram();
1178        if (program == PROGRAM_ERROR) {
1179            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram program error");
1180            glDeleteShader(vertex);
1181            glDeleteShader(fragment);
1182            return PROGRAM_ERROR;
1183        }
1184
1185        // The gl function has no return value.
1186        glAttachShader(program, vertex);
1187        glAttachShader(program, fragment);
1188        glLinkProgram(program);
1189
1190        GLint linked;
1191        glGetProgramiv(program, GL_LINK_STATUS, &linked);
1192        if (linked != 0) {
1193            glDeleteShader(vertex);
1194            glDeleteShader(fragment);
1195            return program;
1196        }
1197
1198        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "createProgram linked error");
1199        GLint infoLen = 0;
1200        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
1201        if (infoLen > 1) {
1202            char *infoLog = (char *)malloc(sizeof(char) * (infoLen + 1));
1203            memset(infoLog, 0, infoLen + 1);
1204            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
1205            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "glLinkProgram error = %s", infoLog);
1206            free(infoLog);
1207            infoLog = nullptr;
1208        }
1209        glDeleteShader(vertex);
1210        glDeleteShader(fragment);
1211        glDeleteProgram(program);
1212        return PROGRAM_ERROR;
1213    }
1214    } // namespace
1215
1216    bool EGLRender::SetUpEGLContext(void *window) {
1217        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "EglContextInit execute");
1218        eglWindow_ = (EGLNativeWindowType)(window);
1219        // Initialize the display.
1220        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1221        if (eglDisplay_ == EGL_NO_DISPLAY) {
1222            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglGetDisplay: unable to get EGL display");
1223            return false;
1224        }
1225        EGLint majorVersion;
1226        EGLint minorVersion;
1227        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
1228            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1229                        "eglInitialize: unable to get initialize EGL display");
1230            return false;
1231        };
1232        // Select the configuration.
1233        const EGLint maxConfigSize = 1;
1234        EGLint numConfigs;
1235        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
1236            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglChooseConfig: unable to choose configs");
1237            return false;
1238        };
1239        // Create an environment.
1240        // Create a surface.
1241        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
1242        if (eglSurface_ == nullptr) {
1243            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1244                        "eglCreateWindowSurface: unable to create Surface");
1245            return false;
1246        }
1247        if (eglSurface_ == nullptr) {
1248            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender",
1249                        "eglCreateWindowSurface: unable to create Surface");
1250            return false;
1251        }
1252        // Create a context.
1253        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
1254        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
1255            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "eglMakeCurrent failed");
1256            return false;
1257        }
1258        // Create a program.
1259        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
1260        if (program_ == PROGRAM_ERROR) {
1261            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "CreateProgram: unable to create program");
1262            return false;
1263        }
1264        return true;
1265    }
1266
1267    GLint EGLRender::PrepareDraw() {
1268        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
1269            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
1270            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "PrepareDraw: param error");
1271            return POSITION_ERROR;
1272        }
1273
1274        // These gl functions have no return value.
1275        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
1276        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
1277        glClear(GL_COLOR_BUFFER_BIT);
1278        glUseProgram(program_);
1279
1280        return glGetAttribLocation(program_, POSITION_NAME);
1281    }
1282
1283    // Draw a five-pointed star.
1284    void EGLRender::DrawStar(bool drawColor) {
1285        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLRender", "Draw");
1286        GLint position = PrepareDraw();
1287        if (position == POSITION_ERROR) {
1288            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw get position failed");
1289            return;
1290        }
1291
1292        // Draw the background.
1293        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES)) {
1294            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw background failed");
1295            return;
1296        }
1297
1298        // Divide it into five quadrilaterals and calculate the vertices for one of the quadrilaterals.
1299        GLfloat rotateX = 0;
1300        GLfloat rotateY = FIFTY_PERCENT * height_;
1301        GLfloat centerX = 0;
1302        // Convert angles 54° and 18° to radians.
1303        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
1304        // Convert angle 18° to radians.
1305        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
1306        GLfloat leftY = 0;
1307        // Convert angle 18° to radians.
1308        GLfloat rightX = rotateY * (M_PI / 180 * 18);
1309        GLfloat rightY = 0;
1310
1311        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
1312        const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_,  leftY / height_,
1313                                        rotateX / width_, rotateY / height_, rightX / width_, rightY / height_};
1314        auto color = drawColor ? DRAW_COLOR : CHANGE_COLOR;
1315        if (!ExecuteDraw(position, color, shapeVertices)) {
1316            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed");
1317            return;
1318        }
1319
1320        // Convert angle 72° to radians.
1321        GLfloat rad = M_PI / 180 * 72;
1322        // Rotate four times.
1323        for (int i = 0; i < 4; ++i) {
1324            // Obtain the vertices for the other four quadrilaterals through rotation.
1325            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
1326            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
1327            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
1328
1329            // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
1330            const GLfloat shapeVertices[] = {centerX / width_, centerY / height_, leftX / width_,  leftY / height_,
1331                                            rotateX / width_, rotateY / height_, rightX / width_, rightY / height_};
1332
1333            // Draw the shape.
1334            if (!ExecuteDraw(position, color, shapeVertices)) {
1335                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw execute draw shape failed");
1336                return;
1337            }
1338        }
1339        // Submit the drawing command to the GPU. After the GPU completes the execution, the rendering result is displayed on the screen.
1340        glFlush();
1341        glFinish();
1342        if (!eglSwapBuffers(eglDisplay_, eglSurface_)) {
1343            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "Draw FinishDraw failed");
1344            return;
1345        }
1346    }
1347
1348    bool EGLRender::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[]) {
1349        if ((position > 0) || (color == nullptr)) {
1350            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLRender", "ExecuteDraw: param error");
1351            return false;
1352        }
1353
1354        // These gl functions have no return value.
1355        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
1356        glEnableVertexAttribArray(position);
1357        glVertexAttrib4fv(1, color);
1358        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
1359        glDisableVertexAttribArray(position);
1360
1361        return true;
1362    }
1363
1364    void EGLRender::SetEGLWindowSize(int width, int height) {
1365        width_ = width;
1366        height_ = height;
1367    }
1368
1369    // Release resources.
1370    EGLRender::~EGLRender() {
1371        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
1372            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1373        }
1374
1375        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
1376            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1377        }
1378
1379        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
1380            OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "EGLRender", "Release eglDestroySurface failed");
1381        }
1382    }
1383    ```
1384
13855. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file.
1386
1387    ```CMake
1388    # the minimum version of CMake.
1389    cmake_minimum_required(VERSION 3.5.0)
1390    project(LCNXComponent2)
1391
1392    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
1393
1394    if(DEFINED PACKAGE_FIND_FILE)
1395        include(${PACKAGE_FIND_FILE})
1396    endif()
1397
1398    include_directories(${NATIVERENDER_ROOT_PATH}
1399                        ${NATIVERENDER_ROOT_PATH}/render
1400                        ${NATIVERENDER_ROOT_PATH}/manager)
1401
1402    add_library(nativerender SHARED
1403                render/EGLRender.cpp
1404                manager/plugin_manager.cpp
1405                napi_init.cpp)
1406    find_library(
1407        # Set the name of the path variable.
1408        EGL-lib
1409        # Set the name of the NDK library to search for.
1410        EGL
1411    )
1412
1413    find_library(
1414        # Set the name of the path variable.
1415        GLES-lib
1416        # Set the name of the NDK library to search for.
1417        GLESv3
1418    )
1419
1420    find_library(
1421        # Set the name of the path variable.
1422        hilog-lib
1423        # Set the name of the NDK library to search for.
1424        hilog_ndk.z
1425    )
1426
1427    find_library(
1428        # Set the name of the path variable.
1429        libace-lib
1430        # Set the name of the NDK library to search for.
1431        ace_ndk.z
1432    )
1433
1434    find_library(
1435        # Set the name of the path variable.
1436        libnapi-lib
1437        # Set the name of the NDK library to search for.
1438        ace_napi.z
1439    )
1440
1441    find_library(
1442        # Set the name of the path variable.
1443        libuv-lib
1444        # Set the name of the NDK library to search for.
1445        uv
1446    )
1447
1448    target_link_libraries(nativerender PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so)
1449    ```
1450
1451![Figure](./figures/drawStar.jpeg)
1452
1453## Managing the Surface Lifecycle with NativeXComponent
1454
1455Unlike the previous scenarios, this scenario involves using ArkUI NDK APIs on the native side to create XComponent components for custom rendering. The key steps include: creating components, obtaining **NativeXComponent** instances, registering lifecycle callbacks for the **XComponent** along with touch, mouse, and key event callbacks, obtaining **NativeWindow** instances through these callbacks, performing graphic rendering on **XComponent** components using OpenGL ES/EGL APIs, and finally mounting the display using **ContentSlot** placeholder components in the ArkTS layer. The development mainly involves the following use cases:
1456
1457- Register the lifecycle and event callbacks of the **XComponent**.
1458- Initialize the environment, obtain the current state, and respond to various events via these callbacks.
1459- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and allocate and submit buffers to the graphics queue.
1460
1461**Constraints**
1462
1463When constructing **XComponent** components, be sure to select the appropriate node type that meets your service requirements.
1464
1465> **NOTE**
1466>
1467> 1. **OH_NativeXComponent** instances are cached in a dictionary on the native side. Their keys must be globally unique, and they must be promptly removed from the dictionary when the corresponding **XComponent** component is destroyed.
1468>
1469> 2. During development with multiple **XComponent** components, make sure the keys used to cache resources on the Native side are unique. The two recommended key formats are as follows: **Id** + random number; **surfaceId**.
1470
1471**lifecycle Callbacks**
1472
1473- OnSurfaceCreated
1474
1475  Triggered when the surface of the **XComponent** component is ready.
1476
1477  ArkTS-side sequence
1478
1479  ![OnSurfaceCreated](./figures/onSurfaceCreated.png)
1480
1481- OnSurfaceChanged
1482
1483  Triggered after surface size changes trigger re-layout.
1484
1485  ArkTS-side sequence
1486
1487  ![OnSurfaceChanged](./figures/onSurfaceChanged.png)
1488
1489- OnSurfaceDestroyed
1490
1491  Triggered when the **XComponent** component is destroyed, which is consistent with the destruction timing of common ArkUI components.
1492
1493  ArkTS-side sequence
1494
1495  ![OnSurfaceDestroyed](./figures/onSurfaceDestroyed.png)
1496
1497**Available APIs**
1498
1499| API                                                      | Description                                                        |
1500| ------------------------------------------------------------ | ------------------------------------------------------------ |
1501| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | Obtains the ID of an **XComponent**.                                        |
1502| 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**.                         |
1503| 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.     |
1504| 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/capi-oh-nativexcomponent-native-xcomponent-oh-nativexcomponent-touchevent.md).|
1505| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | Obtains the tool type of an **XComponent** touch point.                            |
1506| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | Obtains the tilt of an **XComponent** touch point relative to the x-axis.                   |
1507| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | Obtains the tilt of an **XComponent** touch point relative to the y-axis.                   |
1508| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | Obtains the mouse event triggered by an **XComponent**.                            |
1509| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | Registers a lifecycle or touch event callback for an **OH_NativeXComponent** instance.     |
1510| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | Registers the mouse event callback for an **OH_NativeXComponent** instance.               |
1511| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus obtaining event callback for an **OH_NativeXComponent** instance.           |
1512| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the key event callback for an **OH_NativeXComponent** instance.               |
1513| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus loss event callback for an **OH_NativeXComponent** instance.           |
1514| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | Obtains the key event triggered by an **XComponent**.                            |
1515| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | Obtains the action of a key event.                                        |
1516| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | Obtains the key code value of a key event.                                      |
1517| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | Obtains the input source type of a key event.                                  |
1518| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | Obtains the device ID of a key event.                                      |
1519| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | Obtains the timestamp of a key event.                                      |
1520| OH_ArkUI_QueryModuleInterfaceByName(ArkUI_NativeAPIVariantKind type, const char* structName) | Obtains the native API set of a specified type.                          |
1521| OH_ArkUI_GetNodeContentFromNapiValue(napi_env env, napi_value value, ArkUI_NodeContentHandle* content) | Obtains a **NodeContent** object on the ArkTS side and maps it to an **ArkUI_NodeContentHandle** object on the native side.|
1522| OH_ArkUI_NodeContent_SetUserData(ArkUI_NodeContentHandle content, void* userData) | Saves custom data to the specified **NodeContent** object.                         |
1523| OH_ArkUI_NodeContentEvent_GetNodeContentHandle(ArkUI_NodeContentEvent* event) | Obtains the object that triggers the specified **NodeContent** event.                             |
1524| OH_ArkUI_NodeContent_GetUserData(ArkUI_NodeContentHandle content) | Obtains the custom data saved on the specified **NodeContent** object.                   |
1525| OH_ArkUI_NodeContentEvent_GetEventType(ArkUI_NodeContentEvent* event) | Obtains the type of the specified **NodeContent** event.                         |
1526| OH_ArkUI_NodeContent_AddNode(ArkUI_NodeContentHandle content, ArkUI_NodeHandle node) | Adds an ArkUI component node to the specified **NodeContent** object.          |
1527| OH_ArkUI_NodeContent_RegisterCallback(ArkUI_NodeContentHandle content, ArkUI_NodeContentCallback callback) | Registers an event callback for the **NodeContent**.                                   |
1528| OH_NativeXComponent_GetNativeXComponent(ArkUI_NodeHandle node) | Obtains a pointer of the **OH_NativeXComponent** type based on the specified component instance created by the native API.|
1529| OH_NativeXComponent_GetHistoricalPoints(OH_NativeXComponent* component, const void* window, int32_t* size, OH_NativeXComponent_HistoricalPoint** historicalPoints ) | Obtains the historical touch point data for the touch event of an **OH_NativeXComponent** instance. Some input devices report touch points at very high frequencies (up to 1 ms intervals). However, since UI updates typically do not require such high-frequency updates, the system consolidates touch events and reports them once per frame. All touch points collected during the current frame are preserved as historical touch points for applications that need direct access to this raw data. For details about the specifications of historical touch points, see [Resampling and Historical Points](arkts-interaction-development-guide-touch-screen.md#resampling-and-historical-points).|
1530
1531> **NOTE**
1532>
1533> The preceding APIs do not support cross-thread access.
1534>
1535> 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.
1536
1537**How to Develop**
1538
1539The 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.
1540
15411. Define the **XComponent** on the UI.
1542
1543    ```typescript
1544    import nativeNode from 'libnativenode.so';
1545    import {NodeContent} from '@kit.ArkUI';
1546
1547    @Entry
1548    @Component
1549    struct Index {
1550      @State currentStatus: string = "init";
1551      private nodeContent: NodeContent = new NodeContent();
1552      aboutToAppear():void{
1553        // Create a node through the C API and add it to the nodeContent manager.
1554        nativeNode.createNativeNode(this.nodeContent);
1555      }
1556
1557      build() {
1558        Column() {
1559          Row() {
1560            Text('Native XComponent Sample')
1561            .fontSize('24fp')
1562            .fontWeight(500)
1563            .margin({
1564                left: 24,
1565                top: 12
1566            })
1567          }
1568          .margin({ top: 24 })
1569          .width('100%')
1570          .height(56)
1571
1572          Column({ space: 10 }) {
1573            // Display the native components stored in the nodeContent manager.
1574            ContentSlot(this.nodeContent);
1575
1576            Text(this.currentStatus)
1577            .fontSize('24fp')
1578            .fontWeight(500)
1579          }
1580          .onClick(() => {
1581            let hasChangeColor: boolean = false;
1582            // Obtain the current drawing state.
1583            if (nativeNode.getStatus()) {
1584              hasChangeColor = nativeNode.getStatus().hasChangeColor;
1585            }
1586            if (hasChangeColor) {
1587              this.currentStatus = "change color";
1588            }
1589          })
1590          .margin({
1591            top: 27,
1592            left: 12,
1593            right: 12
1594          })
1595          .height('40%')
1596          .width('90%')
1597
1598          Row() {
1599            Button('Draw Star')
1600            .fontSize('16fp')
1601            .fontWeight(500)
1602            .margin({ bottom: 24 })
1603            .onClick(() => {
1604              // Call drawPattern to draw content.
1605              nativeNode.drawPattern();
1606              let hasDraw: boolean = false;
1607              // Obtain the current drawing state.
1608              if (nativeNode.getStatus()) {
1609                hasDraw = nativeNode.getStatus().hasDraw;
1610              }
1611              if (hasDraw) {
1612                this.currentStatus = "draw star";
1613              }
1614            })
1615            .width('53.6%')
1616            .height(40)
1617          }
1618          .width('100%')
1619          .justifyContent(FlexAlign.Center)
1620          .alignItems(VerticalAlign.Bottom)
1621          .layoutWeight(1)
1622        }
1623        .width('100%')
1624        .height('100%')
1625      }
1626    }
1627    ```
1628
16292. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md).
1630
1631    ```c++
1632    #include <hilog/log.h>
1633    #include "common/common.h"
1634    #include "manager/plugin_manager.h"
1635
1636    // 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.
1637    EXTERN_C_START
1638    static napi_value Init(napi_env env, napi_value exports) {
1639        // ...
1640        // Expose APIs to the ArkTS side.
1641        napi_property_descriptor desc[] = {
1642            {"createNativeNode", nullptr, PluginManager::createNativeNode, nullptr, nullptr, nullptr,
1643            napi_default, nullptr },
1644            {"getStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr,
1645            nullptr, napi_default, nullptr},
1646            {"drawPattern", nullptr, PluginManager::NapiDrawPattern, nullptr, nullptr,
1647            nullptr, napi_default, nullptr}
1648        };
1649        if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) {
1650            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed");
1651            return nullptr;
1652        }
1653        return exports;
1654    }
1655    EXTERN_C_END
1656
1657    // Provide module descriptor configuration. You can modify parameters as needed.
1658    static napi_module nativerenderModule = {
1659        .nm_version = 1,
1660        .nm_flags = 0,
1661        .nm_filename = nullptr,
1662        // Entry point function
1663        .nm_register_func = Init,// Specify the callback for when the corresponding module is loaded.
1664        // Module name
1665        .nm_modname =
1666            "nativerender", // Specify the module name. The name must be the same as the value of libraryname in the XComponent on the ArkTS side.
1667        .nm_priv = ((void *)0),
1668        .reserved = {0}};
1669
1670    // 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 descriptor configuration for module registration.
1671    extern "C" __attribute__((constructor)) void RegisterModule(void) { napi_module_register(&nativerenderModule); }
1672    ```
1673
16743. Register the **XComponent** event callback and use the Node-API to implement it.
1675
1676    (1) Define the callbacks for the touch event of the **XComponent** component and for when a surface is successfully created, changed, or destroyed.
1677
1678    ```c++
1679    // Define the PluginManager class in the header file.
1680    class PluginManager {
1681    public:
1682        static OH_NativeXComponent_Callback callback_;
1683        PluginManager();
1684        ~PluginManager();
1685        static PluginManager* GetInstance()
1686        {
1687            return &PluginManager::pluginManager_;
1688        }
1689
1690        static napi_value createNativeNode(napi_env env, napi_callback_info info);
1691        static napi_value GetXComponentStatus(napi_env env, napi_callback_info info);
1692        static napi_value NapiDrawPattern(napi_env env, napi_callback_info info);
1693
1694        // CAPI XComponent
1695        void OnSurfaceChanged(OH_NativeXComponent* component, void* window);
1696        void OnSurfaceDestroyed(OH_NativeXComponent* component, void* window);
1697        void DispatchTouchEvent(OH_NativeXComponent* component, void* window);
1698        void OnSurfaceCreated(OH_NativeXComponent* component, void* window);
1699
1700    public:
1701        EGLCore *eglcore_;
1702        uint64_t width_;
1703        uint64_t height_;
1704        OH_NativeXComponent_TouchEvent touchEvent_;
1705        static int32_t hasDraw_;
1706        static int32_t hasChangeColor_;
1707
1708    private:
1709        static PluginManager pluginManager_;
1710        std::unordered_map<std::string, OH_NativeXComponent*> nativeXComponentMap_;
1711        std::unordered_map<std::string, PluginManager*> pluginManagerMap_;
1712    };
1713    ```
1714
1715    ```c++
1716    // Define the OnSurfaceCreatedCB() function to encapsulate the initialization environment and drawing background.
1717    void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
1718        // ...
1719        // Initialize the environment and draw the background.
1720        auto *pluginManger = PluginManager::GetInstance();
1721        pluginManger->OnSurfaceCreated(component, window);
1722    }
1723
1724    // Define the OnSurfaceChangedCB() function.
1725    void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
1726        // ...
1727        auto *pluginManger = PluginManager::GetInstance();
1728        // Encapsulate the OnSurfaceChanged method.
1729        pluginManger->OnSurfaceChanged(component, window);
1730    }
1731
1732    // Define the OnSurfaceDestroyedCB() function and encapsulate in it the Release() method in the PluginRender class for releasing resources.
1733    void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
1734        // ...
1735        auto *pluginManger = PluginManager::GetInstance();
1736        pluginManger->OnSurfaceDestroyed(component, window);
1737    }
1738
1739    // Define the DispatchTouchEventCB() function, which is triggered to respond to a touch event.
1740    void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) {
1741        // ...
1742        auto *pluginManger = PluginManager::GetInstance();
1743        pluginManger->DispatchTouchEvent(component, window);
1744    }
1745    ```
1746
1747    (2) Define the **createNativeNode** API, which will be called by the **createNativeNode()** API exposed to the ArkTS side.
1748
1749    ```c++
1750    napi_value PluginManager::createNativeNode(napi_env env, napi_callback_info info)
1751    {
1752        if ((env == nullptr) || (info == nullptr)) {
1753            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode env or info is null");
1754            return nullptr;
1755        }
1756        size_t argCnt = 2;
1757        napi_value args[2] = { nullptr, nullptr };
1758        if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
1759            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "CreateNativeNode napi_get_cb_info failed");
1760        }
1761        if (argCnt != ARG_CNT) {
1762            napi_throw_type_error(env, NULL, "Wrong number of arguments");
1763            return nullptr;
1764        }
1765        ArkUI_NodeContentHandle nodeContentHandle_ = nullptr;
1766        OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &nodeContentHandle_);
1767        nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1*>(
1768            OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")
1769        );
1770        std::string tag = value2String(env, args[1]);
1771        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "tag=%{public}s", tag.c_str());
1772        int32_t ret = OH_ArkUI_NodeContent_SetUserData(nodeContentHandle_, new std::string(tag));
1773        if (ret != ARKUI_ERROR_CODE_NO_ERROR) {
1774            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "setUserData failed error=%{public}d", ret);
1775        }
1776        if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
1777            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager",
1778                        "CreateNativeNode tag=%{public}s", tag.c_str());
1779            auto nodeContentEvent = [](ArkUI_NodeContentEvent *event) {
1780                ArkUI_NodeContentHandle handle = OH_ArkUI_NodeContentEvent_GetNodeContentHandle(event);
1781                std::string *userDate = reinterpret_cast<std::string*>(OH_ArkUI_NodeContent_GetUserData(handle));
1782                if (OH_ArkUI_NodeContentEvent_GetEventType(event) == NODE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW) {
1783                    ArkUI_NodeHandle testNode;
1784                    if (userDate) {
1785                        testNode = CreateNodeHandle(*userDate);
1786                        delete userDate;
1787                        userDate = nullptr;
1788                    } else {
1789                        testNode = CreateNodeHandle("noUserData");
1790                    }
1791                    OH_ArkUI_NodeContent_AddNode(handle, testNode);
1792                }
1793            };
1794            OH_ArkUI_NodeContent_RegisterCallback(nodeContentHandle_, nodeContentEvent);
1795        }
1796        return nullptr;
1797    }
1798
1799    ArkUI_NodeHandle CreateNodeHandle(const std::string &tag)
1800    {
1801        ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN);
1802        ArkUI_NumberValue value[] = {480};
1803        ArkUI_NumberValue value1[] = {{.u32 = 15}, {.f32 = 15}};
1804        ArkUI_AttributeItem item = {value, 1, "changeSize"};
1805        ArkUI_AttributeItem item1 = {value1, 2};
1806        nodeAPI->setAttribute(column, NODE_WIDTH, &item);
1807        value[0].f32 = COLUMN_MARGIN;
1808        nodeAPI->setAttribute(column, NODE_MARGIN, &item);
1809        // Create a XComponent component.
1810        xc = nodeAPI->createNode(ARKUI_NODE_XCOMPONENT);
1811        // Set XComponent attributes.
1812        value[0].u32 = ARKUI_XCOMPONENT_TYPE_SURFACE;
1813        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_TYPE, &item);
1814        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_ID, &item);
1815        nodeAPI->setAttribute(xc, NODE_XCOMPONENT_SURFACE_SIZE, &item1);
1816        ArkUI_NumberValue focusable[] = {1};
1817        focusable[0].i32 = 1;
1818        ArkUI_AttributeItem focusableItem = {focusable, 1};
1819        nodeAPI->setAttribute(xc, NODE_FOCUSABLE, &focusableItem);
1820        ArkUI_NumberValue valueSize[] = {480};
1821        ArkUI_AttributeItem itemSize = {valueSize, 1};
1822        valueSize[0].f32 = XC_WIDTH;
1823        nodeAPI->setAttribute(xc, NODE_WIDTH, &itemSize);
1824        valueSize[0].f32 = XC_HEIGHT;
1825        nodeAPI->setAttribute(xc, NODE_HEIGHT, &itemSize);
1826        ArkUI_AttributeItem item2 = {value, 1, "ndkxcomponent"};
1827        nodeAPI->setAttribute(xc, NODE_ID, &item2);
1828
1829        auto *nativeXComponent = OH_NativeXComponent_GetNativeXComponent(xc);
1830        if (!nativeXComponent) {
1831            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent error");
1832            return column;
1833        }
1834        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "GetNativeXComponent success");
1835        // Register XComponent callbacks.
1836        OH_NativeXComponent_RegisterCallback(nativeXComponent, &PluginManager::callback_);
1837        auto typeRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_TYPE);
1838        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent type: %{public}d",
1839                    typeRet->value[0].i32);
1840        auto idRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_ID);
1841        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "xcomponent id: %{public}s",
1842                    idRet->string);
1843        nodeAPI->addChild(column, xc);
1844        return column;
1845    }
1846    ```
1847
1848    (3) Define the **NapiDrawPattern** API, which will be called by the **drawPattern()** API exposed to the ArkTS side.
1849
1850    ```c++
1851    napi_value PluginManager::NapiDrawPattern(napi_env env, napi_callback_info info) {
1852        // ...
1853        // Obtain environment variables.
1854        napi_value thisArg;
1855        if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) {
1856            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "NapiDrawPattern: napi_get_cb_info fail");
1857            return nullptr;
1858        }
1859
1860        auto *pluginManger = PluginManager::GetInstance();
1861        // Call the drawing API.
1862        pluginManger->eglcore_->Draw(hasDraw_);
1863        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginManager", "render->eglCore_->Draw() executed");
1864
1865        return nullptr;
1866    }
1867    ```
1868
18694. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering surface, and creating and associating the context.
1870
1871    ```c++
1872    void EGLCore::UpdateSize(int width, int height) {
1873        // width_ and height_ are defined in the header file.
1874        width_ = width;
1875        height_ = height;
1876        if (width_ > 0) {
1877            widthPercent_ = FIFTY_PERCENT * height_ / width_;
1878        }
1879    }
1880
1881    bool EGLCore::EglContextInit(void *window, int width, int height) {
1882        // ...
1883        UpdateSize(width, height);
1884        eglWindow_ = static_cast<EGLNativeWindowType>(window);
1885
1886        // Initialize the display.
1887        eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
1888        if (eglDisplay_ == EGL_NO_DISPLAY) {
1889            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display");
1890            return false;
1891        }
1892
1893        // Initialize the EGL.
1894        EGLint majorVersion;
1895        EGLint minorVersion;
1896        if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) {
1897            OH_LOG_Print(
1898                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display");
1899            return false;
1900        }
1901
1902        // Select the configuration.
1903        const EGLint maxConfigSize = 1;
1904        EGLint numConfigs;
1905        if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) {
1906            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs");
1907            return false;
1908        }
1909        // Create an environment.
1910        return CreateEnvironment();
1911        }
1912    ```
1913
1914    ```c++
1915    bool EGLCore::CreateEnvironment() {
1916        // Create a surface.
1917        if (eglWindow_ == nullptr) {
1918            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null");
1919            return false;
1920        }
1921        eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL);
1922        if (eglSurface_ == nullptr) {
1923            OH_LOG_Print(
1924                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface");
1925            return false;
1926        }
1927
1928        // Create a context.
1929        eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS);
1930        if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) {
1931            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed");
1932            return false;
1933        }
1934
1935        // Create a program.
1936        program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER);
1937        if (program_ == PROGRAM_ERROR) {
1938            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program");
1939            return false;
1940        }
1941        return true;
1942    }
1943
1944    GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) {
1945        if ((vertexShader == nullptr) || (fragShader == nullptr)) {
1946            OH_LOG_Print(
1947                LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null");
1948            return PROGRAM_ERROR;
1949        }
1950
1951        GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader);
1952        if (vertex == PROGRAM_ERROR) {
1953            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error");
1954            return PROGRAM_ERROR;
1955        }
1956
1957        GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader);
1958        if (fragment == PROGRAM_ERROR) {
1959            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error");
1960            return PROGRAM_ERROR;
1961        }
1962
1963        GLuint program = glCreateProgram();
1964        if (program == PROGRAM_ERROR) {
1965            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error");
1966            glDeleteShader(vertex);
1967            glDeleteShader(fragment);
1968            return PROGRAM_ERROR;
1969        }
1970
1971        // These gl functions have no return value.
1972        glAttachShader(program, vertex);
1973        glAttachShader(program, fragment);
1974        glLinkProgram(program);
1975
1976        GLint linked;
1977        glGetProgramiv(program, GL_LINK_STATUS, &linked);
1978        if (linked != 0) {
1979            glDeleteShader(vertex);
1980            glDeleteShader(fragment);
1981            return program;
1982        }
1983
1984        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error");
1985        GLint infoLen = 0;
1986        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
1987        if (infoLen > 1) {
1988            char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
1989            memset(infoLog, 0, infoLen + 1);
1990            glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
1991            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog);
1992            free(infoLog);
1993            infoLog = nullptr;
1994        }
1995        glDeleteShader(vertex);
1996        glDeleteShader(fragment);
1997        glDeleteProgram(program);
1998        return PROGRAM_ERROR;
1999    }
2000
2001    GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) {
2002        if ((type <= 0) || (shaderSrc == nullptr)) {
2003            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error");
2004            return PROGRAM_ERROR;
2005        }
2006
2007        GLuint shader = glCreateShader(type);
2008        if (shader == 0) {
2009            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader");
2010            return PROGRAM_ERROR;
2011        }
2012
2013        // These gl functions have no return value.
2014        glShaderSource(shader, 1, &shaderSrc, nullptr);
2015        glCompileShader(shader);
2016
2017        GLint compiled;
2018        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
2019        if (compiled != 0) {
2020            return shader;
2021        }
2022
2023        GLint infoLen = 0;
2024        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
2025        if (infoLen <= 1) {
2026            glDeleteShader(shader);
2027            return PROGRAM_ERROR;
2028        }
2029
2030        char *infoLog = (char*)malloc(sizeof(char) * (infoLen + 1));
2031        if (infoLog != nullptr) {
2032            memset(infoLog, 0, infoLen + 1);
2033            glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
2034            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog);
2035            free(infoLog);
2036            infoLog = nullptr;
2037        }
2038        glDeleteShader(shader);
2039        return PROGRAM_ERROR;
2040    }
2041
2042    ```
2043
20445. Implement the rendering function.
2045
2046   (1) Draw the background.
2047
2048    ```c++
2049    // ...
2050    // Background color #f4f4f4
2051    const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f };
2052
2053    // Drawing pattern color
2054    const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f};
2055
2056    // Changed drawing pattern color
2057    const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f};
2058
2059    // Vertices for the background rectangle
2060    const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = {
2061        -1.0f, 1.0f,
2062        1.0f, 1.0f,
2063        1.0f, -1.0f,
2064        -1.0f, -1.0f
2065    };
2066    // ...
2067    ```
2068
2069    ```c++
2070    // Background color
2071    void EGLCore::Background() {
2072        GLint position = PrepareDraw();
2073        if (position == POSITION_ERROR) {
2074            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed");
2075            return;
2076        }
2077
2078        if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES,
2079                         sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2080            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed");
2081            return;
2082        }
2083
2084        if (!FinishDraw()) {
2085            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed");
2086            return;
2087        }
2088    }
2089
2090    // Prepare for drawing and obtain the value of position. When the creation is successful, the value of position starts from 0.
2091    GLint EGLCore::PrepareDraw() {
2092        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) ||
2093            (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) {
2094            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error");
2095            return POSITION_ERROR;
2096        }
2097
2098        // These gl functions have no return value.
2099        glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_);
2100        glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT);
2101        glClear(GL_COLOR_BUFFER_BIT);
2102        glUseProgram(program_);
2103
2104        return glGetAttribLocation(program_, POSITION_NAME);
2105    }
2106
2107    // Draw a specified color in the specified area based on the input parameters.
2108    bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], unsigned long vertSize) {
2109        if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
2110            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
2111            return false;
2112        }
2113
2114        // These gl functions have no return value.
2115        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
2116        glEnableVertexAttribArray(position);
2117        glVertexAttrib4fv(1, color);
2118        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
2119        glDisableVertexAttribArray(position);
2120
2121        return true;
2122    }
2123
2124    // End the drawing operation.
2125    bool EGLCore::FinishDraw() {
2126        // Forcibly flush the buffer.
2127        glFlush();
2128        glFinish();
2129        return eglSwapBuffers(eglDisplay_, eglSurface_);
2130    }
2131    ```
2132
2133   (2) Draw the shape.
2134
2135    ```c++
2136    void EGLCore::Draw(int& hasDraw) {
2137        flag_ = false;
2138        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw");
2139        GLint position = PrepareDraw();
2140        if (position == POSITION_ERROR) {
2141            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed");
2142            return;
2143        }
2144
2145        // Draw the background.
2146        if (!ExecuteDraw(position, BACKGROUND_COLOR,
2147                        BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2148            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed");
2149            return;
2150        }
2151
2152        // Divide the pentagon into five quadrilaterals and calculate the four vertices of one of the quadrilaterals.
2153        GLfloat rotateX = 0;
2154        GLfloat rotateY = FIFTY_PERCENT * height_;
2155        GLfloat centerX = 0;
2156        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
2157        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
2158        GLfloat leftY = 0;
2159        GLfloat rightX = rotateY * (M_PI / 180 * 18);
2160        GLfloat rightY = 0;
2161
2162        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
2163        const GLfloat shapeVertices[] = {
2164            centerX / width_, centerY / height_,
2165            leftX / width_, leftY / height_,
2166            rotateX / width_, rotateY / height_,
2167            rightX / width_, rightY / height_
2168        };
2169
2170        if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
2171            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
2172            return;
2173        }
2174
2175        GLfloat rad = M_PI / 180 * 72;
2176        for (int i = 0; i < NUM_4; ++i) {
2177            // Obtain the vertices for the other four quadrilaterals through rotation.
2178            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
2179            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
2180            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
2181
2182            // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
2183            const GLfloat shapeVertices[] = {
2184                centerX / width_, centerY / height_,
2185                leftX / width_, leftY / height_,
2186                rotateX / width_, rotateY / height_,
2187                rightX / width_, rightY / height_
2188            };
2189
2190            // Draw the shape.
2191            if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) {
2192                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed");
2193                return;
2194            }
2195        }
2196
2197        // End drawing.
2198        if (!FinishDraw()) {
2199            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed");
2200            return;
2201        }
2202        hasDraw = 1;
2203
2204        flag_ = true;
2205    }
2206    ```
2207
2208   (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.
2209
2210    ```c++
2211    void EGLCore::ChangeColor(int& hasChangeColor) {
2212        if (!flag_) {
2213            return;
2214        }
2215        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor");
2216        GLint position = PrepareDraw();
2217        if (position == POSITION_ERROR) {
2218            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed");
2219            return;
2220        }
2221
2222        // Draw the background.
2223        if (!ExecuteDraw(position, BACKGROUND_COLOR,
2224                        BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) {
2225            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed");
2226            return;
2227        }
2228
2229        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
2230        GLfloat rotateX = 0;
2231        GLfloat rotateY = FIFTY_PERCENT * height_;
2232        GLfloat centerX = 0;
2233        GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18);
2234        GLfloat leftX = -rotateY * (M_PI / 180 * 18);
2235        GLfloat leftY = 0;
2236        GLfloat rightX = rotateY * (M_PI / 180 * 18);
2237        GLfloat rightY = 0;
2238
2239        // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
2240        const GLfloat shapeVertices[] = {
2241            centerX / width_, centerY / height_,
2242            leftX / width_, leftY / height_,
2243            rotateX / width_, rotateY / height_,
2244            rightX / width_, rightY / height_
2245        };
2246
2247        // Use the new colors for drawing.
2248        if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
2249            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed");
2250            return;
2251        }
2252
2253        GLfloat rad = M_PI / 180 * 72;
2254        for (int i = 0; i < NUM_4; ++i) {
2255            // Obtain the vertices for the other four quadrilaterals through rotation.
2256            Rotate2d(centerX, centerY, &rotateX, &rotateY, rad);
2257            Rotate2d(centerX, centerY, &leftX, &leftY, rad);
2258            Rotate2d(centerX, centerY, &rightX, &rightY, rad);
2259
2260            // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area.
2261            const GLfloat shapeVertices[] = {
2262                centerX / width_, centerY / height_,
2263                leftX / width_, leftY / height_,
2264                rotateX / width_, rotateY / height_,
2265                rightX / width_, rightY / height_
2266            };
2267
2268            // Use the new colors for drawing.
2269            if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) {
2270                OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed");
2271                return;
2272            }
2273        }
2274
2275        // End drawing.
2276        if (!FinishDraw()) {
2277            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed");
2278        }
2279        hasChangeColor = 1;
2280    }
2281
2282   bool EGLCore::ExecuteDrawNewStar(
2283       GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) {
2284       if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) {
2285            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error");
2286            return false;
2287        }
2288
2289        // These gl functions have no return value.
2290        glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices);
2291        glEnableVertexAttribArray(position);
2292        glVertexAttrib4fv(1, color);
2293        glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE);
2294        glDisableVertexAttribArray(position);
2295
2296       return true;
2297   }
2298    ```
2299
23006. Release related resources.
2301
2302    (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.
2303
2304    ```c++
2305    void EGLCore::Release() {
2306        // Release the surface.
2307        if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) {
2308            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed");
2309        }
2310        // Release the context.
2311        if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) {
2312            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed");
2313        }
2314        // Release the display.
2315        if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) {
2316            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed");
2317        }
2318    }
2319    ```
2320
23217. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file.
2322
2323    ```CMake
2324    # Set the minimum CMake version.
2325    cmake_minimum_required(VERSION 3.4.1)
2326    # Project name
2327    project(XComponent)
2328
2329    set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
2330    add_definitions(-DOHOS_PLATFORM)
2331    # Set the header file search directory.
2332    include_directories(
2333        ${NATIVERENDER_ROOT_PATH}
2334        ${NATIVERENDER_ROOT_PATH}/include
2335    )
2336    # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files.
2337    add_library(nativerender SHARED
2338        render/egl_core.cpp
2339        render/plugin_render.cpp
2340        manager/plugin_manager.cpp
2341        napi_init.cpp
2342    )
2343
2344    find_library(
2345        EGL-lib
2346        EGL
2347    )
2348
2349    find_library(
2350        GLES-lib
2351        GLESv3
2352    )
2353
2354    find_library(
2355        hilog-lib
2356        hilog_ndk.z
2357    )
2358
2359    find_library(
2360        libace-lib
2361        ace_ndk.z
2362    )
2363
2364    find_library(
2365        libnapi-lib
2366        ace_napi.z
2367    )
2368
2369    find_library(
2370        libuv-lib
2371        uv
2372    )
2373    # Add the libraries to be linked.
2374    target_link_libraries(nativerender PUBLIC
2375        ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib})
2376    ```
2377
2378<!--RP1--><!--RP1End-->
2379<!--no_check-->