1# Custom Rendering (XComponent) 2 3## Overview 4 5The **XComponent** is a rendering component that can be used for EGL/OpenGL ES and media data output. It uses a unique [NativeWindow](../graphics/native-window-guidelines.md) to render graphics and is typically employed to meet complex custom rendering needs, such as displaying camera preview streams and rendering game graphics. You can specify different rendering methods through the **type** field, which are [XComponentType](../reference/apis-arkui/arkui-ts/ts-appendix-enums.md#xcomponenttype10).SURFACE and XComponentType.TEXTURE. For the SURFACE type, you display the custom drawing content on the screen separately. For the TEXTURE type, you combine custom drawing content with the content of the **XComponent** and display it on the screen. 6 7The **XComponent** is mainly used in two scenarios. In the native XComponent scenario, the native layer is responsible for obtaining the native **XComponent** instance and registering the lifecycle callbacks of the **XComponent** along with touch, mouse, and key event callbacks. In the ArkTS XComponent scenario, the **SurfaceId** is obtained on the ArkTS side, with lifecycle callbacks, touch, mouse, and key event callbacks all being managed and triggered there. 8 9## Native XComponent Scenario 10Specify the dynamic library to be loaded with the **libraryname** parameter of the **XComponent** constructor. The application can then obtain the native **XComponent** instance at the native layer. This instance, provided by the **XComponent** itself, acts as a bridge, facilitating interactions between the ArkTS layer and the native layer. The NDK APIs provided by the **XComponent** all depend on this instance. The provided APIs include those for obtaining a **NativeWindow** instance, obtaining the layout or event information of the **XComponent**, registering the lifecycle callbacks of the **XComponent**, and registering the callbacks for the touch, mouse, and key events of the **XComponent**. You can use the provided APIs in the following scenarios: 11 12- Register the lifecycle and event callbacks of the **XComponent**. 13- Initialize the environment, obtain the current state, and respond to various events via these callbacks. 14- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue. 15 16**Available APIs** 17 18| API | Description | 19| ------------------------------------------------------------ | ------------------------------------------------------------ | 20| OH_NativeXComponent_GetXComponentId(OH_NativeXComponent* component, char* id, uint64_t* size) | Obtains the ID of an **XComponent**. | 21| OH_NativeXComponent_GetXComponentSize(OH_NativeXComponent* component, const void* window, uint64_t* width, uint64_t* height) | Obtains the size of the surface held by an **XComponent**. | 22| OH_NativeXComponent_GetXComponentOffset(OH_NativeXComponent* component, const void* window, double* x, double* y) | Obtains the offset of the surface held by the **XComponent** relative to the upper left corner of its parent component. | 23| OH_NativeXComponent_GetTouchEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_TouchEvent* touchEvent) | Obtains the touch event triggered by an **XComponent**. For details about the attribute values in **touchEvent**, see [OH_NativeXComponent_TouchEvent](../reference/apis-arkui/_o_h___native_x_component___touch_event.md).| 24| OH_NativeXComponent_GetTouchPointToolType(OH_NativeXComponent* component, uint32_t pointIndex, OH_NativeXComponent_TouchPointToolType* toolType) | Obtains the tool type of an **XComponent** touch point. | 25| OH_NativeXComponent_GetTouchPointTiltX(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltX) | Obtains the tilt of an **XComponent** touch point relative to the x-axis. | 26| OH_NativeXComponent_GetTouchPointTiltY(OH_NativeXComponent* component, uint32_t pointIndex, float* tiltY) | Obtains the tilt of an **XComponent** touch point relative to the y-axis. | 27| OH_NativeXComponent_GetMouseEvent(OH_NativeXComponent* component, const void* window, OH_NativeXComponent_MouseEvent* mouseEvent) | Obtains the mouse event triggered by an **XComponent**. | 28| OH_NativeXComponent_RegisterCallback(OH_NativeXComponent* component, OH_NativeXComponent_Callback* callback) | Registers a lifecycle or touch event callback for an **OH_NativeXComponent** instance. | 29| OH_NativeXComponent_RegisterMouseEventCallback(OH_NativeXComponent* component, OH_NativeXComponent_MouseEvent_Callback* callback) | Registers the mouse event callback for an **OH_NativeXComponent** instance. | 30| OH_NativeXComponent_RegisterFocusEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus obtaining event callback for an **OH_NativeXComponent** instance. | 31| OH_NativeXComponent_RegisterKeyEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the key event callback for an **OH_NativeXComponent** instance. | 32| OH_NativeXComponent_RegisterBlurEventCallback(OH_NativeXComponent* component, void (\*callback)(OH_NativeXComponent* component, void* window)) | Registers the focus loss event callback for an **OH_NativeXComponent** instance. | 33| OH_NativeXComponent_GetKeyEvent(OH_NativeXComponent* component, OH_NativeXComponent_KeyEvent\** keyEvent) | Obtains the key event triggered by an **XComponent**. | 34| OH_NativeXComponent_GetKeyEventAction(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyAction* action) | Obtains the action of a key event. | 35| OH_NativeXComponent_GetKeyEventCode(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_KeyCode* code) | Obtains the key code value of a key event. | 36| OH_NativeXComponent_GetKeyEventSourceType(OH_NativeXComponent_KeyEvent* keyEvent, OH_NativeXComponent_EventSourceType* sourceType) | Obtains the input source type of a key event. | 37| OH_NativeXComponent_GetKeyEventDeviceId(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* deviceId) | Obtains the device ID of a key event. | 38| OH_NativeXComponent_GetKeyEventTimestamp(OH_NativeXComponent_KeyEvent* keyEvent, int64_t* timestamp) | Obtains the timestamp of a key event. | 39 40> **NOTE** 41> 42> The preceding APIs do not support cross-thread access. 43> 44> When the XComponent is destroyed (after the **onSurfaceDestroyed** callback is triggered), the **OH_NativeXComponent** and window objects obtained through the preceding APIs will be released. If these object are used again, crashes may occur due to the use of dangling or null pointers. 45 46**How to Develop** 47 48The following uses the SURFACE type as an example to describe how to use the **XComponent** to call the Node-API to create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color. 49 501. Define the **XComponent** on the UI. 51 52 ```typescript 53 // API declaration 54 export default interface XComponentContext { 55 drawPattern(): void; 56 57 getStatus(): XComponentContextStatus; 58 }; 59 60 type XComponentContextStatus = { 61 hasDraw: boolean, 62 hasChangeColor: boolean, 63 }; 64 ``` 65 66 ```typescript 67 @Entry 68 @Component 69 struct Index { 70 @State message: string = 'Hello World' 71 xComponentContext: object | undefined = undefined; 72 xComponentAttrs: XComponentAttrs = { 73 id: 'xcomponentId', 74 type: XComponentType.SURFACE, 75 libraryname: 'nativerender' 76 } 77 78 build() { 79 Row() { 80 // ... 81 // Define XComponent in an .ets file. 82 XComponent(this.xComponentAttrs) 83 .focusable(true) // Set the component to be able to respond to key events. 84 .onLoad((xComponentContext) => { 85 this.xComponentContext = xComponentContext; 86 }) 87 .onDestroy(() => { 88 console.log("onDestroy"); 89 }) 90 // ... 91 } 92 .height('100%') 93 } 94 } 95 96 interface XComponentAttrs { 97 id: string; 98 type: number; 99 libraryname: string; 100 } 101 ``` 102 1032. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md). 104 105 ```c++ 106 // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call. 107 EXTERN_C_START 108 static napi_value Init(napi_env env, napi_value exports) 109 { 110 // ... 111 // Expose the getContext() API to the ArkTS code. 112 napi_property_descriptor desc[] = { 113 { "getContext", nullptr, PluginManager::GetContext, nullptr, nullptr, nullptr, napi_default, nullptr } 114 }; 115 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 116 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 117 return nullptr; 118 } 119 // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API. 120 PluginManager::GetInstance()->Export(env, exports); 121 return exports; 122 } 123 EXTERN_C_END 124 125 // Write the API description. You can modify parameters as required. 126 static napi_module nativerenderModule = { 127 .nm_version = 1, 128 .nm_flags = 0, 129 .nm_filename = nullptr, 130 // Entry point function 131 .nm_register_func = Init,// Specify the callback for when the corresponding module is loaded. 132 // Module name 133 .nm_modname = "nativerender", // Specify the module name. For XComponent-related development, the name must be the same as the value of libraryname in the XComponent on ArkTS. 134 .nm_priv = ((void *)0), 135 .reserved = { 0 } 136 }; 137 138 // The method decorated by __attribute__((constructor)) is automatically called by the system. The Node-API napi_module_register() is used to pass in the module description for module registration. 139 extern "C" __attribute__((constructor)) void RegisterModule(void) 140 { 141 napi_module_register(&nativerenderModule); 142 } 143 ``` 144 ```c++ 145 // Check whether the environment variable contains an instance of XComponent. If the instance exists, export the drawing-related API. 146 void PluginManager::Export(napi_env env, napi_value exports) 147 { 148 // ... 149 // Obtain a native XComponent. 150 OH_NativeXComponent* nativeXComponent = nullptr; 151 if (napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent)) != napi_ok) { 152 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: napi_unwrap fail"); 153 return; 154 } 155 156 // Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code. 157 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 158 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 159 if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { 160 OH_LOG_Print( 161 LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "Export: OH_NativeXComponent_GetXComponentId fail"); 162 return; 163 } 164 165 std::string id(idStr); 166 auto context = PluginManager::GetInstance(); 167 if ((context != nullptr) && (nativeXComponent != nullptr)) { 168 context->SetNativeXComponent(id, nativeXComponent); 169 auto render = context->GetRender(id); 170 if (render != nullptr) { 171 // Register the callbacks. 172 render->RegisterCallback(nativeXComponent); 173 // Use Node-API in the method to export drawing-related APIs to expose them to the ArkTS side. 174 render->Export(env, exports); 175 } 176 } 177 } 178 ``` 179 ```c++ 180 // Use the napi_define_properties method to expose the drawPattern() method to the ArkTS side, which allows the drawPattern() method to be called in ArkTS code. 181 void PluginRender::Export(napi_env env, napi_value exports) 182 { 183 // ... 184 // Register the function as the ArkTS API drawPattern. 185 napi_property_descriptor desc[] = { 186 { "drawPattern", nullptr, PluginRender::NapiDrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr } 187 }; 188 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 189 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "Export: napi_define_properties failed"); 190 } 191 } 192 ``` 193 1943. Register the **XComponent** event callback and use the Node-API to implement it. 195 196 (1) Define the callbacks for the touch event of the **XComponent** and for when a surface is successfully created, changed, or destroyed. 197 198 ```c++ 199 // Define the PluginRender class. 200 class PluginRender { 201 public: 202 explicit PluginRender(std::string& id); 203 ~PluginRender() 204 { 205 if (eglCore_ != nullptr) { 206 eglCore_->Release(); 207 delete eglCore_; 208 eglCore_ = nullptr; 209 } 210 } 211 static PluginRender* GetInstance(std::string& id); 212 static void Release(std::string& id); 213 static napi_value NapiDrawPattern(napi_env env, napi_callback_info info); 214 void Export(napi_env env, napi_value exports); 215 void OnSurfaceChanged(OH_NativeXComponent* component, void* window); 216 void OnTouchEvent(OH_NativeXComponent* component, void* window); 217 void OnMouseEvent(OH_NativeXComponent* component, void* window); 218 void OnHoverEvent(OH_NativeXComponent* component, bool isHover); 219 void OnFocusEvent(OH_NativeXComponent* component, void* window); 220 void OnBlurEvent(OH_NativeXComponent* component, void* window); 221 void OnKeyEvent(OH_NativeXComponent* component, void* window); 222 void RegisterCallback(OH_NativeXComponent* nativeXComponent); 223 224 public: 225 static std::unordered_map<std::string, PluginRender*> instance_; 226 EGLCore* eglCore_; 227 static int32_t hasDraw_; 228 static int32_t hasChangeColor_; 229 230 private: 231 OH_NativeXComponent_Callback renderCallback_; 232 OH_NativeXComponent_MouseEvent_Callback mouseCallback_; 233 }; 234 235 std::unordered_map<std::string, PluginRender*> PluginRender::instance_; 236 int32_t PluginRender::hasDraw_ = 0; 237 int32_t PluginRender::hasChangeColor_ = 0; 238 239 PluginRender* PluginRender::GetInstance(std::string& id) 240 { 241 if (instance_.find(id) == instance_.end()) { 242 PluginRender* instance = new PluginRender(id); 243 instance_[id] = instance; 244 return instance; 245 } else { 246 return instance_[id]; 247 } 248 } 249 250 // Define the OnSurfaceCreatedCB() function to encapsulate the initialization environment and drawing background. 251 void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) 252 { 253 // ... 254 // Obtain the ID of the XComponent, that is, the id parameter in the XComponent struct in the ArkTS code. 255 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 256 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 257 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NativeXComponent_RESULT_SUCCESS) { 258 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 259 "OnSurfaceCreatedCB: Unable to get XComponent id"); 260 return; 261 } 262 263 // Initialize the environment and draw the background. 264 std::string id(idStr); 265 auto render = PluginRender::GetInstance(id); 266 uint64_t width; 267 uint64_t height; 268 // Obtain the size of the surface held by the XComponent. 269 int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 270 if ((xSize == OH_NativeXComponent_RESULT_SUCCESS) && (render != nullptr)) { 271 if (render->eglCore_->EglContextInit(window, width, height)) { 272 render->eglCore_->Background(); 273 } 274 } 275 } 276 277 // Define the OnSurfaceChangedCB() function. 278 void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) 279 { 280 // ... 281 // Obtain the ID of the XComponent. 282 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 283 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 284 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NativeXComponent_RESULT_SUCCESS) { 285 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 286 "OnSurfaceChangedCB: Unable to get XComponent id"); 287 return; 288 } 289 290 std::string id(idStr); 291 auto render = PluginRender::GetInstance(id); 292 if (render != nullptr) { 293 // Encapsulate the OnSurfaceChanged method. 294 render->OnSurfaceChanged(component, window); 295 } 296 } 297 298 // Define the OnSurfaceDestroyedCB() function and encapsulate in it the Release() method in the PluginRender class for releasing resources. 299 void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) 300 { 301 // ... 302 // Obtain the ID of the XComponent. 303 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 304 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 305 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NativeXComponent_RESULT_SUCCESS) { 306 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 307 "OnSurfaceDestroyedCB: Unable to get XComponent id"); 308 return; 309 } 310 311 std::string id(idStr); 312 // Release resources. 313 PluginRender::Release(id); 314 } 315 316 // Define the DispatchTouchEventCB() function, which is triggered to respond to a touch event. 317 void DispatchTouchEventCB(OH_NativeXComponent *component, void *window) 318 { 319 // ... 320 // Obtain the ID of the XComponent. 321 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 322 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 323 if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NativeXComponent_RESULT_SUCCESS) { 324 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", 325 "DispatchTouchEventCB: Unable to get XComponent id"); 326 return; 327 } 328 329 std::string id(idStr); 330 PluginRender *render = PluginRender::GetInstance(id); 331 if (render != nullptr) { 332 // Encapsulate the OnTouchEvent method. 333 render->OnTouchEvent(component, window); 334 } 335 } 336 337 // Define the DispatchMouseEventCB() function, which is triggered when a mouse event is responded to. 338 void DispatchMouseEventCB(OH_NativeXComponent *component, void *window) { 339 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchMouseEventCB"); 340 int32_t ret; 341 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 342 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 343 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 344 if (ret != OH_NativeXComponent_RESULT_SUCCESS) { 345 return; 346 } 347 348 std::string id(idStr); 349 auto render = PluginRender::GetInstance(id); 350 if (render) { 351 // Encapsulate the OnMouseEvent method. 352 render->OnMouseEvent(component, window); 353 } 354 } 355 356 // Define the DispatchHoverEventCB() function, which is triggered when the mouse pointer hover event is responded to. 357 void DispatchHoverEventCB(OH_NativeXComponent *component, bool isHover) { 358 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "DispatchHoverEventCB"); 359 int32_t ret; 360 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 361 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 362 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 363 if (ret != OH_NativeXComponent_RESULT_SUCCESS) { 364 return; 365 } 366 367 std::string id(idStr); 368 auto render = PluginRender::GetInstance(id); 369 if (render) { 370 // Encapsulate the OnHoverEvent method. 371 render->OnHoverEvent(component, isHover); 372 } 373 } 374 375 // Define the OnFocusEventCB() function, which is triggered when a focus obtaining event is responded to. 376 void OnFocusEventCB(OH_NativeXComponent *component, void *window) { 377 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnFocusEventCB"); 378 int32_t ret; 379 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 380 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 381 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 382 if (ret != OH_NativeXComponent_RESULT_SUCCESS) { 383 return; 384 } 385 386 std::string id(idStr); 387 auto render = PluginRender::GetInstance(id); 388 if (render) { 389 // Encapsulate the OnFocusEvent method. 390 render->OnFocusEvent(component, window); 391 } 392 } 393 394 // Define the OnBlurEventCB() function, which is triggered when the focus loss event is responded to. 395 void OnBlurEventCB(OH_NativeXComponent *component, void *window) { 396 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnBlurEventCB"); 397 int32_t ret; 398 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 399 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 400 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 401 if (ret != OH_NativeXComponent_RESULT_SUCCESS) { 402 return; 403 } 404 405 std::string id(idStr); 406 auto render = PluginRender::GetInstance(id); 407 if (render) { 408 // Encapsulate the OnBlurEvent method. 409 render->OnBlurEvent(component, window); 410 } 411 } 412 413 // Define the OnKeyEventCB() function, which is triggered when a key event is responded to. 414 void OnKeyEventCB(OH_NativeXComponent *component, void *window) { 415 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback", "OnKeyEventCB"); 416 int32_t ret; 417 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {}; 418 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 419 ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize); 420 if (ret != OH_NativeXComponent_RESULT_SUCCESS) { 421 return; 422 } 423 std::string id(idStr); 424 auto render = PluginRender::GetInstance(id); 425 if (render) { 426 // Encapsulate the OnKeyEvent method. 427 render->OnKeyEvent(component, window); 428 } 429 } 430 431 // Define an OnSurfaceChanged() method. 432 void PluginRender::OnSurfaceChanged(OH_NativeXComponent* component, void* window) 433 { 434 // ... 435 std::string id(idStr); 436 PluginRender* render = PluginRender::GetInstance(id); 437 double offsetX; 438 double offsetY; 439 // Obtain the offset of the surface held by the XComponent relative to the upper left corner of its parent component. 440 OH_NativeXComponent_GetXComponentOffset(component, window, &offsetX, &offsetY); 441 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OH_NativeXComponent_GetXComponentOffset", 442 "offsetX = %{public}lf, offsetY = %{public}lf", offsetX, offsetY); 443 uint64_t width; 444 uint64_t height; 445 OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); 446 if (render != nullptr) { 447 render->eglCore_->UpdateSize(width, height); 448 } 449 } 450 451 // Define an OnTouchEvent() method. 452 void PluginRender::OnTouchEvent(OH_NativeXComponent* component, void* window) 453 { 454 // ... 455 OH_NativeXComponent_TouchEvent touchEvent; 456 // Obtain the touch event triggered by the XComponent. 457 OH_NativeXComponent_GetTouchEvent(component, window, &touchEvent); 458 // Obtain the x coordinate of the XComponent touch point relative to the left edge of the XComponent and the y coordinate of the XComponent touch point relative to the upper edge of the XComponent. 459 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 460 "touch info: x = %{public}lf, y = %{public}lf", touchEvent.x, touchEvent.y); 461 // Obtain the x coordinate and y-coordinate of the XComponent touch point relative to the upper left corner of the application window where the XComponent is located. 462 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 463 "touch info: screenX = %{public}lf, screenY = %{public}lf", touchEvent.screenX, touchEvent.screenY); 464 std::string id(idStr); 465 PluginRender* render = PluginRender::GetInstance(id); 466 if (render != nullptr && touchEvent.type == OH_NativeXComponent_TouchEventType::OH_NativeXComponent_UP) { 467 render->eglCore_->ChangeColor(); 468 hasChangeColor_ = 1; 469 } 470 float tiltX = 0.0f; 471 float tiltY = 0.0f; 472 OH_NativeXComponent_TouchPointToolType toolType = 473 OH_NativeXComponent_TouchPointToolType::OH_NativeXComponent_TOOL_TYPE_UNKNOWN; 474 // Obtain the tool type of the XComponent touch point. 475 OH_NativeXComponent_GetTouchPointToolType(component, 0, &toolType); 476 // Obtain the tilt of the XComponent touch point relative to the x-axis. 477 OH_NativeXComponent_GetTouchPointTiltX(component, 0, &tiltX); 478 // Obtain the tilt of the XComponent touch point relative to the y-axis. 479 OH_NativeXComponent_GetTouchPointTiltY(component, 0, &tiltY); 480 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "OnTouchEvent", 481 "touch info: toolType = %{public}d, tiltX = %{public}lf, tiltY = %{public}lf", toolType, tiltX, tiltY); 482 } 483 484 // Define an OnMouseEvent() method. 485 void PluginRender::OnMouseEvent(OH_NativeXComponent *component, void *window) { 486 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnMouseEvent"); 487 OH_NativeXComponent_MouseEvent mouseEvent; 488 // Obtain the mouse event triggered by the XComponent. 489 int32_t ret = OH_NativeXComponent_GetMouseEvent(component, window, &mouseEvent); 490 if (ret == OH_NativeXComponent_RESULT_SUCCESS) { 491 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "MouseEvent Info: x = %{public}f, y = %{public}f, action = %{public}d, button = %{public}d", mouseEvent.x, mouseEvent.y, mouseEvent.action, mouseEvent.button); 492 } else { 493 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetMouseEvent error"); 494 } 495 } 496 497 // Define an OnHoverEvent() method. 498 void PluginRender::OnHoverEvent(OH_NativeXComponent* component, bool isHover) 499 { 500 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnHoverEvent isHover_ = %{public}d", isHover); 501 } 502 503 // Define an OnFocusEvent() method. 504 void PluginRender::OnFocusEvent(OH_NativeXComponent* component, void* window) 505 { 506 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnFocusEvent"); 507 } 508 509 // Define an OnBlurEvent() method. 510 void PluginRender::OnBlurEvent(OH_NativeXComponent* component, void* window) 511 { 512 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnBlurEvent"); 513 } 514 515 // Define an OnKeyEvent() method. 516 void PluginRender::OnKeyEvent(OH_NativeXComponent *component, void *window) { 517 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "OnKeyEvent"); 518 519 OH_NativeXComponent_KeyEvent *keyEvent = nullptr; 520 // Obtain the key event triggered by the XComponent. 521 if (OH_NativeXComponent_GetKeyEvent(component, &keyEvent) >= 0) { 522 OH_NativeXComponent_KeyAction action; 523 // Obtain the action of a key event. 524 OH_NativeXComponent_GetKeyEventAction(keyEvent, &action); 525 OH_NativeXComponent_KeyCode code; 526 // Obtain the key code value of a key event. 527 OH_NativeXComponent_GetKeyEventCode(keyEvent, &code); 528 OH_NativeXComponent_EventSourceType sourceType; 529 // Obtain the input source type of a key event. 530 OH_NativeXComponent_GetKeyEventSourceType(keyEvent, &sourceType); 531 int64_t deviceId; 532 // Obtain the device ID of a key event. 533 OH_NativeXComponent_GetKeyEventDeviceId(keyEvent, &deviceId); 534 int64_t timeStamp; 535 // Obtain the timestamp of a key event. 536 OH_NativeXComponent_GetKeyEventTimestamp(keyEvent, &timeStamp); 537 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "KeyEvent Info: action=%{public}d, code=%{public}d, sourceType=%{public}d, deviceId=%{public}ld, timeStamp=%{public}ld", action, code, sourceType, deviceId, timeStamp); 538 } else { 539 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "GetKeyEvent error"); 540 } 541 } 542 ``` 543 544 (2) Register the **XComponent** event callback and call the method defined in step 3.1 when the **XComponent** event is triggered. 545 546 ```c++ 547 void PluginRender::RegisterCallback(OH_NativeXComponent *NativeXComponent) { 548 // Set the callback of the component creation event. When the component is created, related operations are triggered to initialize the environment and draw the background. 549 renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; 550 // Set the callback of the component change event. When the component changes, related operations are triggered. 551 renderCallback_.OnSurfaceChanged = OnSurfaceChangedCB; 552 // Set the callback of the component destruction event. When the component is destroyed, related operations are triggered to release the requested resources. 553 renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; 554 // Set the callback of the touch event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 555 renderCallback_.DispatchTouchEvent = DispatchTouchEventCB; 556 // Register OH_NativeXComponent_Callback with NativeXComponent. 557 OH_NativeXComponent_RegisterCallback(NativeXComponent, &renderCallback_); 558 559 // Set the callback of the mouse event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 560 mouseCallback_.DispatchMouseEvent = DispatchMouseEventCB; 561 // Set the callback of the mouse hover event. When the touch event is triggered, the Node-API is called to invoke the embedded C++ method. 562 mouseCallback_.DispatchHoverEvent = DispatchHoverEventCB; 563 // Register OH_NativeXComponent_MouseEvent_Callback with NativeXComponent. 564 OH_NativeXComponent_RegisterMouseEventCallback(NativeXComponent, &mouseCallback_); 565 566 // Register the OnFocusEventCB method with NativeXComponent. 567 OH_NativeXComponent_RegisterFocusEventCallback(NativeXComponent, OnFocusEventCB); 568 // Register the OnKeyEventCB method with NativeXComponent. 569 OH_NativeXComponent_RegisterKeyEventCallback(NativeXComponent, OnKeyEventCB); 570 // Register the OnBlurEventCB method with NativeXComponent. 571 OH_NativeXComponent_RegisterBlurEventCallback(NativeXComponent, OnBlurEventCB); 572 } 573 ``` 574 575 (3) Define the **NapiDrawPattern** method, which will be called by the **drawPattern()** method exposed to the ArkTS side. 576 577 ```c++ 578 napi_value PluginRender::NapiDrawPattern(napi_env env, napi_callback_info info) 579 { 580 // ... 581 // Obtain environment variables. 582 napi_value thisArg; 583 if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { 584 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_get_cb_info fail"); 585 return nullptr; 586 } 587 588 // Obtain the XComponent instance from the environment variables. 589 napi_value exportInstance; 590 if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { 591 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 592 "NapiDrawPattern: napi_get_named_property fail"); 593 return nullptr; 594 } 595 596 // Use napi_unwrap to obtain the pointer to the XComponent instance. 597 OH_NativeXComponent *NativeXComponent = nullptr; 598 if (napi_unwrap(env, exportInstance, reinterpret_cast<void **>(&NativeXComponent)) != napi_ok) { 599 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", "NapiDrawPattern: napi_unwrap fail"); 600 return nullptr; 601 } 602 603 // Obtain the ID of the XComponent. 604 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' }; 605 uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 606 if (OH_NativeXComponent_GetXComponentId(NativeXComponent, idStr, &idSize) != OH_NativeXComponent_RESULT_SUCCESS) { 607 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginRender", 608 "NapiDrawPattern: Unable to get XComponent id"); 609 return nullptr; 610 } 611 612 std::string id(idStr); 613 PluginRender *render = PluginRender::GetInstance(id); 614 if (render) { 615 // Call the drawing method. 616 render->eglCore_->Draw(); 617 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "PluginRender", "render->eglCore_->Draw() executed"); 618 } 619 return nullptr; 620 } 621 ``` 622 6234. Initialize the environment, including initializing the available EGLDisplay, determining the available surface configuration, creating the rendering area surface, and creating and associating the context. 624 625 ```c++ 626 void EGLCore::UpdateSize(int width, int height) 627 { 628 width_ = width; 629 height_ = height; 630 } 631 632 bool EGLCore::EglContextInit(void *window, int width, int height) 633 { 634 // ... 635 UpdateSize(width, height); 636 eglWindow_ = static_cast<EGLNativeWindowType>(window); 637 638 // Initialize the display. 639 eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); 640 if (eglDisplay_ == EGL_NO_DISPLAY) { 641 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); 642 return false; 643 } 644 645 // Initialize the EGL. 646 EGLint majorVersion; 647 EGLint minorVersion; 648 if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { 649 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", 650 "eglInitialize: unable to get initialize EGL display"); 651 return false; 652 } 653 654 // Select the configuration. 655 const EGLint maxConfigSize = 1; 656 EGLint numConfigs; 657 if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { 658 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); 659 return false; 660 } 661 662 // Create an environment. 663 return CreateEnvironment(); 664 } 665 ``` 666 667 ```c++ 668 bool EGLCore::CreateEnvironment() 669 { 670 // ... 671 // Create a surface. 672 eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); 673 674 // ... 675 // Create a context. 676 eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); 677 if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { 678 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); 679 return false; 680 } 681 682 // Create a program. 683 program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); 684 if (program_ == PROGRAM_ERROR) { 685 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); 686 return false; 687 } 688 return true; 689 } 690 691 GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) 692 { 693 if ((vertexShader == nullptr) || (fragShader == nullptr)) { 694 OH_LOG_Print( 695 LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null"); 696 return PROGRAM_ERROR; 697 } 698 699 GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); 700 if (vertex == PROGRAM_ERROR) { 701 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); 702 return PROGRAM_ERROR; 703 } 704 705 GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); 706 if (fragment == PROGRAM_ERROR) { 707 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); 708 return PROGRAM_ERROR; 709 } 710 711 GLuint program = glCreateProgram(); 712 if (program == PROGRAM_ERROR) { 713 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); 714 glDeleteShader(vertex); 715 glDeleteShader(fragment); 716 return PROGRAM_ERROR; 717 } 718 719 // The gl function has no return value. 720 glAttachShader(program, vertex); 721 glAttachShader(program, fragment); 722 glLinkProgram(program); 723 724 GLint linked; 725 glGetProgramiv(program, GL_LINK_STATUS, &linked); 726 if (linked != 0) { 727 glDeleteShader(vertex); 728 glDeleteShader(fragment); 729 return program; 730 } 731 732 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); 733 GLint infoLen = 0; 734 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); 735 if (infoLen > 1) { 736 char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 737 memset(infoLog, 0, infoLen + 1); 738 glGetProgramInfoLog(program, infoLen, nullptr, infoLog); 739 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); 740 free(infoLog); 741 infoLog = nullptr; 742 } 743 glDeleteShader(vertex); 744 glDeleteShader(fragment); 745 glDeleteProgram(program); 746 return PROGRAM_ERROR; 747 } 748 749 GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) 750 { 751 if ((type <= 0) || (shaderSrc == nullptr)) { 752 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); 753 return PROGRAM_ERROR; 754 } 755 756 GLuint shader = glCreateShader(type); 757 if (shader == 0) { 758 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); 759 return PROGRAM_ERROR; 760 } 761 762 // The gl function has no return value. 763 glShaderSource(shader, 1, &shaderSrc, nullptr); 764 glCompileShader(shader); 765 766 GLint compiled; 767 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 768 if (compiled != 0) { 769 return shader; 770 } 771 772 GLint infoLen = 0; 773 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 774 if (infoLen <= 1) { 775 glDeleteShader(shader); 776 return PROGRAM_ERROR; 777 } 778 779 char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); 780 if (infoLog != nullptr) { 781 memset(infoLog, 0, infoLen + 1); 782 glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); 783 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); 784 free(infoLog); 785 infoLog = nullptr; 786 } 787 glDeleteShader(shader); 788 return PROGRAM_ERROR; 789 } 790 791 ``` 792 7935. Implement the rendering function. 794 795 (1) Draw the background. 796 797 ```c++ 798 // Draw the background color #f4f4f4. 799 const GLfloat BACKGROUND_COLOR[] = { 244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f }; 800 801 // Draw the background vertex. 802 const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { 803 -1.0f, 1.0f, 804 1.0f, 1.0f, 805 1.0f, -1.0f, 806 -1.0f, -1.0f 807 }; 808 ``` 809 810 ```c++ 811 // Draw the background color. 812 void EGLCore::Background() 813 { 814 GLint position = PrepareDraw(); 815 if (position == POSITION_ERROR) { 816 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); 817 return; 818 } 819 820 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 821 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 822 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); 823 return; 824 } 825 826 if (!FinishDraw()) { 827 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); 828 return; 829 } 830 } 831 832 // Prepare for drawing and obtain the value of position. When the creation is successful, the value of position starts from 0. 833 GLint EGLCore::PrepareDraw() 834 { 835 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || 836 (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { 837 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); 838 return POSITION_ERROR; 839 } 840 841 glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); 842 glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); 843 glClear(GL_COLOR_BUFFER_BIT); 844 glUseProgram(program_); 845 846 return glGetAttribLocation(program_, POSITION_NAME); 847 } 848 849 // Draw a specified color in the specified area based on the input parameters. 850 bool EGLCore::ExecuteDraw(GLint position, const GLfloat *color, const GLfloat shapeVertices[], 851 unsigned long vertSize) 852 { 853 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0]) != SHAPE_VERTICES_SIZE)) { 854 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 855 return false; 856 } 857 858 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 859 glEnableVertexAttribArray(position); 860 glVertexAttrib4fv(1, color); 861 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 862 glDisableVertexAttribArray(position); 863 864 return true; 865 } 866 867 // End the drawing operation. 868 bool EGLCore::FinishDraw() 869 { 870 // Forcibly flush the buffer. 871 glFlush(); 872 glFinish(); 873 return eglSwapBuffers(eglDisplay_, eglSurface_); 874 } 875 ``` 876 877 (2) Draw the shape. 878 879 ```c++ 880 void EGLCore::Draw() 881 { 882 flag_ = false; 883 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); 884 GLint position = PrepareDraw(); 885 if (position == POSITION_ERROR) { 886 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); 887 return; 888 } 889 890 // Draw the background. 891 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 892 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 893 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); 894 return; 895 } 896 897 // Divide the pentagon into five quadrilaterals and calculate the four vertices of one of the quadrilaterals. 898 GLfloat rotateX = 0; 899 GLfloat rotateY = FIFTY_PERCENT * height_; 900 GLfloat centerX = 0; 901 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 902 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 903 GLfloat leftY = 0; 904 GLfloat rightX = rotateY * (M_PI / 180 * 18); 905 GLfloat rightY = 0; 906 907 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 908 const GLfloat shapeVertices[] = { 909 centerX / width_, centerY / height_, 910 leftX / width_, leftY / height_, 911 rotateX / width_, rotateY / height_, 912 rightX / width_, rightY / height_ 913 }; 914 915 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 916 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 917 return; 918 } 919 920 GLfloat rad = M_PI / 180 * 72; 921 for (int i = 0; i < 4; ++i) 922 { 923 // Obtain the vertices of the other four quadrilaterals through rotation. 924 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 925 rotate2d(centerX, centerY, &leftX, &leftY,rad); 926 rotate2d(centerX, centerY, &rightX, &rightY,rad); 927 928 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 929 const GLfloat shapeVertices[] = { 930 centerX / width_, centerY / height_, 931 leftX / width_, leftY / height_, 932 rotateX / width_, rotateY / height_, 933 rightX / width_, rightY / height_ 934 }; 935 936 // Draw the shape. 937 if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { 938 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 939 return; 940 } 941 } 942 943 // End drawing. 944 if (!FinishDraw()) { 945 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); 946 return; 947 } 948 949 flag_ = true; 950 } 951 ``` 952 953 (3) Change the colors, by drawing a new shape with the same size but different colors and replacing the original shape with the new shape. 954 955 ```c++ 956 void EGLCore::ChangeColor() 957 { 958 if (!flag_) { 959 return; 960 } 961 OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); 962 GLint position = PrepareDraw(); 963 if (position == POSITION_ERROR) { 964 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); 965 return; 966 } 967 968 // Draw the background. 969 if (!ExecuteDraw(position, BACKGROUND_COLOR, BACKGROUND_RECTANGLE_VERTICES, 970 sizeof(BACKGROUND_RECTANGLE_VERTICES))) { 971 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); 972 return; 973 } 974 975 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 976 GLfloat rotateX = 0; 977 GLfloat rotateY = FIFTY_PERCENT * height_; 978 GLfloat centerX = 0; 979 GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); 980 GLfloat leftX = -rotateY * (M_PI / 180 * 18); 981 GLfloat leftY = 0; 982 GLfloat rightX = rotateY * (M_PI / 180 * 18); 983 GLfloat rightY = 0; 984 985 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 986 const GLfloat shapeVertices[] = { 987 centerX / width_, centerY / height_, 988 leftX / width_, leftY / height_, 989 rotateX / width_, rotateY / height_, 990 rightX / width_, rightY / height_ 991 }; 992 993 // Use the new colors for drawing. 994 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 995 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 996 return; 997 } 998 999 GLfloat rad = M_PI / 180 * 72; 1000 for (int i = 0; i < 4; ++i) 1001 { 1002 // Obtain the vertices of the other four quadrilaterals through rotation. 1003 rotate2d(centerX, centerY, &rotateX, &rotateY,rad); 1004 rotate2d(centerX, centerY, &leftX, &leftY,rad); 1005 rotate2d(centerX, centerY, &rightX, &rightY,rad); 1006 1007 // Determine the vertices for drawing the quadrilateral, which are represented by the percentages of the drawing area. 1008 const GLfloat shapeVertices[] = { 1009 centerX / width_, centerY / height_, 1010 leftX / width_, leftY / height_, 1011 rotateX / width_, rotateY / height_, 1012 rightX / width_, rightY / height_ 1013 }; 1014 1015 // Use the new colors for drawing. 1016 if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { 1017 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw star failed"); 1018 return; 1019 } 1020 } 1021 1022 // End drawing. 1023 if (!FinishDraw()) { 1024 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); 1025 } 1026 } 1027 1028 bool EGLCore::ExecuteDrawNewStar( 1029 GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) 1030 { 1031 if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { 1032 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); 1033 return false; 1034 } 1035 1036 // The gl function has no return value. 1037 glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); 1038 glEnableVertexAttribArray(position); 1039 glVertexAttrib4fv(1, color); 1040 glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); 1041 glDisableVertexAttribArray(position); 1042 1043 return true; 1044 } 1045 ``` 1046 10476. Release related resources. 1048 1049 (1) Create the **Release()** method in the **EGLCore** class to release the resources requested during environment initialization, including the window display, rendering area surface, and environment context. 1050 1051 ```c++ 1052 void EGLCore::Release() 1053 { 1054 // Release the surface. 1055 if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { 1056 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); 1057 } 1058 // Release the context. 1059 if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { 1060 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); 1061 } 1062 // Release the display. 1063 if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { 1064 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); 1065 } 1066 } 1067 ``` 1068 1069 (2) Add the **Release()** method to the **PluginRender** class to release the **EGLCore** and **PluginRender** instances. 1070 1071 ```c++ 1072 void PluginRender::Release(std::string &id) 1073 { 1074 PluginRender *render = PluginRender::GetInstance(id); 1075 if (render != nullptr) { 1076 render->eglCore_->Release(); 1077 delete render->eglCore_; 1078 render->eglCore_ = nullptr; 1079 delete render; 1080 render = nullptr; 1081 instance_.erase(instance_.find(id)); 1082 } 1083 } 1084 ``` 1085 10867. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file. 1087 1088 ```CMake 1089 # Set the minimum CMake version. 1090 cmake_minimum_required(VERSION 3.4.1) 1091 # Project name 1092 project(XComponent) 1093 1094 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 1095 add_definitions(-DOHOS_PLATFORM) 1096 # Set the header file search directory. 1097 include_directories( 1098 ${NATIVERENDER_ROOT_PATH} 1099 ${NATIVERENDER_ROOT_PATH}/include 1100 ) 1101 # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files. 1102 add_library(nativerender SHARED 1103 render/egl_core.cpp 1104 render/plugin_render.cpp 1105 manager/plugin_manager.cpp 1106 napi_init.cpp 1107 ) 1108 1109 find_library( 1110 EGL-lib 1111 EGL 1112 ) 1113 1114 find_library( 1115 GLES-lib 1116 GLESv3 1117 ) 1118 1119 find_library( 1120 hilog-lib 1121 hilog_ndk.z 1122 ) 1123 1124 find_library( 1125 libace-lib 1126 ace_ndk.z 1127 ) 1128 1129 find_library( 1130 libnapi-lib 1131 ace_napi.z 1132 ) 1133 1134 find_library( 1135 libuv-lib 1136 uv 1137 ) 1138 # Add the libraries to be linked. 1139 target_link_libraries(nativerender PUBLIC 1140 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib}) 1141 ``` 1142 1143## ArkTS XComponent Scenario 1144 1145Unlike the native **XComponent**, the ArkTS **XComponent** does not require the **libraryname** parameter. It obtains the **SurfaceId** on the ArkTS side, and layout information, lifecycle callbacks, touch, mouse, key event callbacks, and more are all triggered on the ArkTS side and passed to the native side for processing as needed. The development mainly involves the following scenarios: 1146- Use the **SurfaceId** obtained on the ArkTS side to call the **OH_NativeWindow_CreateNativeWindowFromSurfaceId** API on the native side to create a **NativeWindow** instance. 1147- Use the **NativeWindow** instance with EGL APIs to develop custom drawing content, and apply for and submit buffers to the graphics queue. 1148- Obtain lifecycle and event information on the ArkTS side and pass it to the native side for processing. 1149 1150**Available APIs** 1151 1152XComponentController on the ArkTS side 1153 1154| API | Description | 1155| ------------------------------------------------------------ | ------------------------------------------------------------ | 1156| getXComponentSurfaceId(): string | Obtains the ID of the surface associated with the **XComponent**. | 1157| onSurfaceCreated(surfaceId: string): void | Called when the surface held by the **XComponent** is created. | 1158| onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void | Called when the size of the surface held by the **XComponent** changes (including the initial size change upon creation).| 1159| onSurfaceDestroyed(surfaceId: string): void | Called when the surface held by the **XComponent** is destroyed. | 1160 1161Native side 1162 1163| API | Description | 1164| ------------------------------------------------------------ | ------------------------------------------------------------ | 1165| int32_t OH_NativeWindow_CreateNativeWindowFromSurfaceId (uint64_t surfaceId, OHNativeWindow **window ) | Creates an **OHNativeWindow** instance based on a surface ID. | 1166| void OH_NativeWindow_DestroyNativeWindow (OHNativeWindow* window) | Decreases the reference count of an **OHNativeWindow** instance by 1 and when the reference count reaches 0, destroys the instance.| 1167 1168**How to Develop** 1169 1170The following uses the SURFACE type as an example to describe how to use the **XComponent** to pass in **SurfaceId** on the ArkTS side, create a **NativeWindow** instance on the native side, then create an EGL/GLES environment, implement drawing graphics on the main page, and change the graphics color. 1171 11721. Define the **XComponent** on the UI. 1173 1174 ```javascript 1175 // Function declaration 1176 type XComponentContextStatus = { 1177 hasDraw: boolean, 1178 hasChangeColor: boolean, 1179 }; 1180 export const SetSurfaceId: (id: BigInt) => any; 1181 export const ChangeSurface: (id: BigInt, w: number, h: number) =>any; 1182 export const DrawPattern: (id: BigInt) => any; 1183 export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus 1184 export const ChangeColor: (id: BigInt) => any; 1185 export const DestroySurface: (id: BigInt) => any; 1186 ``` 1187 1188 ```typescript 1189 import nativeRender from 'libnativerender.so' 1190 1191 // Override XComponentController. 1192 class MyXComponentController extends XComponentController { 1193 onSurfaceCreated(surfaceId: string): void { 1194 console.log(`onSurfaceCreated surfaceId: ${surfaceId}`) 1195 nativeRender.SetSurfaceId(BigInt(surfaceId)); 1196 } 1197 1198 onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { 1199 console.log(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`) 1200 nativeRender.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight) 1201 } 1202 1203 onSurfaceDestroyed(surfaceId: string): void { 1204 console.log(`onSurfaceDestroyed surfaceId: ${surfaceId}`) 1205 nativeRender.DestroySurface(BigInt(surfaceId)) 1206 } 1207 } 1208 1209 @Entry 1210 @Component 1211 struct Index { 1212 @State currentStatus: string = "index"; 1213 xComponentController: XComponentController = new MyXComponentController(); 1214 1215 build() { 1216 Column() { 1217 //... 1218 // Define XComponent in an .ets file. 1219 Column({ space: 10 }) { 1220 XComponent({ 1221 type: XComponentType.SURFACE, 1222 controller: this.xComponentController 1223 }) 1224 Text(this.currentStatus) 1225 .fontSize('24fp') 1226 .fontWeight(500) 1227 } 1228 .onClick(() => { 1229 let surfaceId = this.xComponentController.getXComponentSurfaceId() 1230 nativeRender.ChangeColor(BigInt(surfaceId)) 1231 let hasChangeColor: boolean = false; 1232 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 1233 hasChangeColor = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor; 1234 } 1235 if (hasChangeColor) { 1236 this.currentStatus = "change color"; 1237 } 1238 }) 1239 1240 //... 1241 Row() { 1242 Button('Draw Star') 1243 .fontSize('16fp') 1244 .fontWeight(500) 1245 .margin({ bottom: 24 }) 1246 .onClick(() => { 1247 let surfaceId = this.xComponentController.getXComponentSurfaceId() 1248 nativeRender.DrawPattern(BigInt(surfaceId)) 1249 let hasDraw: boolean = false; 1250 if (nativeRender.GetXComponentStatus(BigInt(surfaceId))) { 1251 hasDraw = nativeRender.GetXComponentStatus(BigInt(surfaceId)).hasDraw; 1252 } 1253 if (hasDraw) { 1254 this.currentStatus = "draw star" 1255 } 1256 }) 1257 .width('53.6%') 1258 .height(40) 1259 } 1260 .width('100%') 1261 .justifyContent(FlexAlign.Center) 1262 .alignItems(VerticalAlign.Bottom) 1263 .layoutWeight(1) 1264 } 1265 .width('100%') 1266 .height('100%') 1267 } 1268 } 1269 ``` 1270 12712. Register the Node-API module. For details, see [Node-API Development Specifications](../napi/napi-guidelines.md). 1272 1273 ```typescript 1274 #include <hilog/log.h> 1275 #include "common/common.h" 1276 #include "manager/plugin_manager.h" 1277 namespace NativeXComponentSample { 1278 // In the napi_init.cpp file, use the Init method to register the target function to pass in the encapsulated C++ methods for the ArkTS method to call. 1279 EXTERN_C_START 1280 static napi_value Init(napi_env env, napi_value exports) { 1281 // ... 1282 // Expose the SetSurfaceId(), ChangeSurface(), and DestroySurface() APIs to the ArkTS side. 1283 // DrawPattern(),ChangeColor(),GetXComponentStatus() 1284 napi_property_descriptor desc[] = { 1285 {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr}, 1286 {"ChangeSurface", nullptr, PluginManager::ChangeSurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 1287 {"DestroySurface", nullptr, PluginManager::DestroySurface, nullptr, nullptr, nullptr, napi_default, nullptr}, 1288 {"DrawPattern", nullptr, PluginManager::DrawPattern, nullptr, nullptr, nullptr, napi_default, nullptr}, 1289 {"ChangeColor", nullptr, PluginManager::ChangeColor, nullptr, nullptr, nullptr, napi_default, nullptr}, 1290 {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, nullptr, nullptr, nullptr, napi_default, 1291 nullptr}}; 1292 if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { 1293 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); 1294 return nullptr; 1295 } 1296 return exports; 1297 } 1298 EXTERN_C_END 1299 // Write the API description. You can modify parameters as required. 1300 static napi_module nativerenderModule = {.nm_version = 1, 1301 .nm_flags = 0, 1302 .nm_filename = nullptr, 1303 // Entry point function 1304 .nm_register_func = Init, 1305 // Module name 1306 .nm_modname = "nativerender", 1307 .nm_priv = ((void *)0), 1308 .reserved = {0}}; 1309 } // namespace NativeXComponentSample 1310 // The method decorated by __attribute__((constructor)) is automatically called by the system. The Node-API napi_module_register() is used to pass in the module description for module registration. 1311 extern "C" __attribute__((constructor)) void RegisterModule(void) { 1312 napi_module_register(&NativeXComponentSample::nativerenderModule); 1313 } 1314 ``` 1315 13163. Implement the preceding six registered functions on the native side. 1317 1318 ```cpp 1319 // Define the PluginManager class. 1320 class PluginManager { 1321 public: 1322 ~PluginManager(); 1323 static PluginRender *GetPluginRender(int64_t &id); 1324 static napi_value ChangeColor(napi_env env, napi_callback_info info); 1325 static napi_value DrawPattern(napi_env env, napi_callback_info info); 1326 static napi_value SetSurfaceId(napi_env env, napi_callback_info info); 1327 static napi_value ChangeSurface(napi_env env, napi_callback_info info); 1328 static napi_value DestroySurface(napi_env env, napi_callback_info info); 1329 static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); 1330 1331 public: 1332 static std::unordered_map<int64_t, PluginRender *> pluginRenderMap_; 1333 static std::unordered_map<int64_t, OHNativeWindow *> windowMap_; 1334 }; 1335 1336 // Parse the surfaceId passed from ArkTS. Here, surfaceId is a 64-bit integer value. 1337 int64_t ParseId(napi_env env, napi_callback_info info) { 1338 if ((env == nullptr) || (info == nullptr)) { 1339 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null"); 1340 return -1; 1341 } 1342 size_t argc = 1; 1343 napi_value args[1] = {nullptr}; 1344 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 1345 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed"); 1346 return -1; 1347 } 1348 int64_t value = 0; 1349 bool lossless = true; 1350 if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) { 1351 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed"); 1352 return -1; 1353 } 1354 return value; 1355 } 1356 1357 // Set SurfaceId and initialize NativeWindow based on SurfaceId. 1358 napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) { 1359 int64_t surfaceId = ParseId(env, info); 1360 OHNativeWindow *nativeWindow; 1361 PluginRender *pluginRender; 1362 if (windowMap_.find(surfaceId) == windowMap_.end()) { 1363 OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); 1364 windowMap_[surfaceId] = nativeWindow; 1365 } 1366 if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { 1367 pluginRender = new PluginRender(surfaceId); 1368 pluginRenderMap_[surfaceId] = pluginRender; 1369 } 1370 pluginRender->InitNativeWindow(nativeWindow); 1371 return nullptr; 1372 } 1373 void PluginRender::InitNativeWindow(OHNativeWindow *window) { 1374 eglCore_->EglContextInit(window); // For details about the EglContextInit implementation, see the "Native XComponent Scenario" section. 1375 } 1376 1377 // Implement surface size changes based on the passed surfaceId, width, and height. 1378 napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) { 1379 if ((env == nullptr) || (info == nullptr)) { 1380 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1381 "ChangeSurface: OnLoad env or info is null"); 1382 return nullptr; 1383 } 1384 int64_t surfaceId = 0; 1385 size_t argc = 3; 1386 napi_value args[3] = {nullptr}; 1387 1388 if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { 1389 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1390 "ChangeSurface: GetContext napi_get_cb_info failed"); 1391 return nullptr; 1392 } 1393 bool lossless = true; 1394 int index = 0; 1395 if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) { 1396 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed"); 1397 return nullptr; 1398 } 1399 double width; 1400 if (napi_ok != napi_get_value_double(env, args[index++], &width)) { 1401 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed"); 1402 return nullptr; 1403 } 1404 double height; 1405 if (napi_ok != napi_get_value_double(env, args[index++], &height)) { 1406 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed"); 1407 return nullptr; 1408 } 1409 auto pluginRender = GetPluginRender(surfaceId); 1410 if (pluginRender == nullptr) { 1411 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed"); 1412 return nullptr; 1413 } 1414 pluginRender->UpdateNativeWindowSize(width, height); 1415 return nullptr; 1416 } 1417 1418 void PluginRender::UpdateNativeWindowSize(int width, int height) { 1419 eglCore_->UpdateSize(width, height); // For details about the UpdateSize implementation, see the "Native XComponent Scenario" section. 1420 if (!hasChangeColor_ && !hasDraw_) { 1421 eglCore_->Background(); // For details about the Background implementation, see the "Native XComponent Scenario" section. 1422 } 1423 } 1424 1425 // Destroy the surface. 1426 napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) { 1427 int64_t surfaceId = ParseId(env, info); 1428 auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId); 1429 if (pluginRenderMapIter != pluginRenderMap_.end()) { 1430 delete pluginRenderMapIter->second; 1431 pluginRenderMap_.erase(pluginRenderMapIter); 1432 } 1433 auto windowMapIter = windowMap_.find(surfaceId); 1434 if (windowMapIter != windowMap_.end()) { 1435 OH_NativeWindow_DestroyNativeWindow(windowMapIter->second); 1436 windowMap_.erase(windowMapIter); 1437 } 1438 return nullptr; 1439 } 1440 1441 // Implement the EGL drawing logic. 1442 napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) { 1443 int64_t surfaceId = ParseId(env, info); 1444 auto pluginRender = GetPluginRender(surfaceId); 1445 if (pluginRender == nullptr) { 1446 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed"); 1447 return nullptr; 1448 } 1449 pluginRender->DrawPattern(); 1450 return nullptr; 1451 } 1452 PluginRender *PluginManager::GetPluginRender(int64_t &id) { 1453 if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) { 1454 return pluginRenderMap_[id]; 1455 } 1456 return nullptr; 1457 } 1458 void PluginRender::DrawPattern() { 1459 eglCore_->Draw(hasDraw_); // For details about the Draw implementation, see the "Native XComponent Scenario" section. 1460 } 1461 1462 // Implement the feature of changing the color of the drawn graphics. 1463 napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) { 1464 int64_t surfaceId = ParseId(env, info); 1465 auto pluginRender = GetPluginRender(surfaceId); 1466 if (pluginRender == nullptr) { 1467 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed"); 1468 return nullptr; 1469 } 1470 pluginRender->ChangeColor (); // For details about the ChangeColor implementation, see the "Native XComponent Scenario" section. 1471 return nullptr; 1472 } 1473 void PluginRender::ChangeColor() { eglCore_->ChangeColor(hasChangeColor_); } 1474 1475 // Obtain the XComponent status and return it to the ArkTS side. 1476 napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) { 1477 int64_t surfaceId = ParseId(env, info); 1478 auto pluginRender = GetPluginRender(surfaceId); 1479 if (pluginRender == nullptr) { 1480 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1481 "GetXComponentStatus: Get pluginRender failed"); 1482 return nullptr; 1483 } 1484 napi_value hasDraw; 1485 napi_value hasChangeColor; 1486 napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw)); 1487 if (ret != napi_ok) { 1488 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1489 "GetXComponentStatus: napi_create_int32 hasDraw_ error"); 1490 return nullptr; 1491 } 1492 ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor)); 1493 if (ret != napi_ok) { 1494 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1495 "GetXComponentStatus: napi_create_int32 hasChangeColor_ error"); 1496 return nullptr; 1497 } 1498 napi_value obj; 1499 ret = napi_create_object(env, &obj); 1500 if (ret != napi_ok) { 1501 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1502 "GetXComponentStatus: napi_create_object error"); 1503 return nullptr; 1504 } 1505 ret = napi_set_named_property(env, obj, "hasDraw", hasDraw); 1506 if (ret != napi_ok) { 1507 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1508 "GetXComponentStatus: napi_set_named_property hasDraw error"); 1509 return nullptr; 1510 } 1511 ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); 1512 if (ret != napi_ok) { 1513 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", 1514 "GetXComponentStatus: napi_set_named_property hasChangeColor error"); 1515 return nullptr; 1516 } 1517 return obj; 1518 } 1519 int32_t PluginRender::HasDraw() { return hasDraw_; } 1520 int32_t PluginRender::HasChangedColor() { return hasChangeColor_; } 1521 ``` 1522 15234. Configure the specific CMakeLists to use the CMake toolchain to compile the C++ source code into a dynamic link library file. 1524 1525 ```cmake 1526 # Set the minimum CMake version. 1527 cmake_minimum_required(VERSION 3.4.1) 1528 # Project name 1529 project(XComponent) 1530 1531 set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 1532 add_definitions(-DOHOS_PLATFORM) 1533 # Set the header file search directory. 1534 include_directories( 1535 ${NATIVERENDER_ROOT_PATH} 1536 ${NATIVERENDER_ROOT_PATH}/include 1537 ) 1538 # Add the **nativerender** dynamic library, with the **libnativerender.so** library file. Add the .cpp files. 1539 add_library(nativerender SHARED 1540 render/egl_core.cpp 1541 render/plugin_render.cpp 1542 manager/plugin_manager.cpp 1543 napi_init.cpp 1544 ) 1545 1546 find_library( 1547 # Sets the name of the path variable. 1548 EGL-lib 1549 # Specifies the name of the NDK library that 1550 # you want CMake to locate. 1551 EGL 1552 ) 1553 1554 find_library( 1555 # Sets the name of the path variable. 1556 GLES-lib 1557 # Specifies the name of the NDK library that 1558 # you want CMake to locate. 1559 GLESv3 1560 ) 1561 1562 find_library( 1563 # Sets the name of the path variable. 1564 hilog-lib 1565 # Specifies the name of the NDK library that 1566 # you want CMake to locate. 1567 hilog_ndk.z 1568 ) 1569 1570 find_library( 1571 # Sets the name of the path variable. 1572 libace-lib 1573 # Specifies the name of the NDK library that 1574 # you want CMake to locate. 1575 ace_ndk.z 1576 ) 1577 1578 find_library( 1579 # Sets the name of the path variable. 1580 libnapi-lib 1581 # Specifies the name of the NDK library that 1582 # you want CMake to locate. 1583 ace_napi.z 1584 ) 1585 1586 find_library( 1587 # Sets the name of the path variable. 1588 libuv-lib 1589 # Specifies the name of the NDK library that 1590 # you want CMake to locate. 1591 uv 1592 ) 1593 # Add the libraries to be linked. 1594 target_link_libraries(nativerender PUBLIC 1595 ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) 1596 ``` 1597 1598 1599## Lifecycle Description 1600 1601You can use the **XComponent** to develop EGL/OpenGL ES rendering by using the following ArkTS code: 1602 1603```typescript 1604@Builder 1605function myComponent() { 1606 XComponent({ id: 'xcomponentId1', type: XComponentType.SURFACE, libraryname: 'nativerender' }) 1607 .onLoad((context) => {}) 1608 .onDestroy(() => {}) 1609} 1610``` 1611 1612### **onLoad** event 1613 1614This event is triggered when the surface of the **XComponent** is ready. 1615 1616**context** parameter: where the native API exposed on the module is mounted. Its usage is similar to the usage of a **context** instance obtained after the module is directly loaded using **import context from "libnativerender.so"**. 1617 1618**Sequence**: 1619 1620 Native XComponent scenario: 1621 1622 The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the native side is shown in the figure below. 1623 1624 1625 1626 ArkTS XComponent scenario: 1627 1628 The triggering of the **onLoad** event is subject to the surface, and its sequence with the **OnSurfaceCreated** on the ArkTS side is shown in the figure below. 1629 1630 1631 1632 1633 1634### **onDestroy** event 1635 1636This event is triggered when the **XComponent** component is destroyed, which is the same as the destruction time of common ArkUI components. 1637 1638**Sequence**: 1639 1640 Native XComponent scenario: 1641 1642 The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the native side is shown in the figure below. 1643 1644 1645 1646 1647 1648 ArkTS XComponent scenario: 1649 1650 The triggering of the **onDestroy** event is subject to the surface, and its sequence with the **OnSurfaceDestroyed** on the ArkTS side is shown in the figure below. 1651 1652 1653 1654