commit 3e5d0d999dc8c035180a63536853ea30b52e0931 Author: JackLee_CN <809262979@qq.com> Date: Mon Nov 24 23:14:38 2025 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d2ff201 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/AppScope/app.json5 b/AppScope/app.json5 new file mode 100644 index 0000000..5eb1d12 --- /dev/null +++ b/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.native3d", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:layered_image", + "label": "$string:app_name" + } +} diff --git a/AppScope/resources/base/element/string.json b/AppScope/resources/base/element/string.json new file mode 100644 index 0000000..ae0f0ed --- /dev/null +++ b/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "Native3D" + } + ] +} diff --git a/AppScope/resources/base/media/background.png b/AppScope/resources/base/media/background.png new file mode 100644 index 0000000..923f2b3 Binary files /dev/null and b/AppScope/resources/base/media/background.png differ diff --git a/AppScope/resources/base/media/foreground.png b/AppScope/resources/base/media/foreground.png new file mode 100644 index 0000000..eb94275 Binary files /dev/null and b/AppScope/resources/base/media/foreground.png differ diff --git a/AppScope/resources/base/media/layered_image.json b/AppScope/resources/base/media/layered_image.json new file mode 100644 index 0000000..fb49920 --- /dev/null +++ b/AppScope/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/build-profile.json5 b/build-profile.json5 new file mode 100644 index 0000000..cd84608 --- /dev/null +++ b/build-profile.json5 @@ -0,0 +1,43 @@ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "targetSdkVersion": "6.0.0(20)", + "compatibleSdkVersion": "6.0.0(20)", + "runtimeOS": "HarmonyOS", + "buildOption": { + "nativeCompiler": "BiSheng", + "strictMode": { + "caseSensitiveCheck": true, + "useNormalizedOHMUrl": true + } + } + } + ], + "buildModeSet": [ + { + "name": "debug", + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "n3d", + "srcPath": "./n3d", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/code-linter.json5 b/code-linter.json5 new file mode 100644 index 0000000..073990f --- /dev/null +++ b/code-linter.json5 @@ -0,0 +1,32 @@ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@security/no-unsafe-aes": "error", + "@security/no-unsafe-hash": "error", + "@security/no-unsafe-mac": "warn", + "@security/no-unsafe-dh": "error", + "@security/no-unsafe-dsa": "error", + "@security/no-unsafe-ecdsa": "error", + "@security/no-unsafe-rsa-encrypt": "error", + "@security/no-unsafe-rsa-sign": "error", + "@security/no-unsafe-rsa-key": "error", + "@security/no-unsafe-dsa-key": "error", + "@security/no-unsafe-dh-key": "error", + "@security/no-unsafe-3des": "error" + } +} \ No newline at end of file diff --git a/hvigor/hvigor-config.json5 b/hvigor/hvigor-config.json5 new file mode 100644 index 0000000..7a7ab89 --- /dev/null +++ b/hvigor/hvigor-config.json5 @@ -0,0 +1,23 @@ +{ + "modelVersion": "6.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | "ultrafine" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + // "optimizationStrategy": "memory" /* Define the optimization strategy. Value: [ "memory" | "performance" ]. Default: "memory" */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/hvigorfile.ts b/hvigorfile.ts new file mode 100644 index 0000000..47113e2 --- /dev/null +++ b/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/n3d/.gitignore b/n3d/.gitignore new file mode 100644 index 0000000..e2713a2 --- /dev/null +++ b/n3d/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/n3d/build-profile.json5 b/n3d/build-profile.json5 new file mode 100644 index 0000000..3d3f158 --- /dev/null +++ b/n3d/build-profile.json5 @@ -0,0 +1,45 @@ +{ + "apiType": "stageMode", + "buildOption": { + "resOptions": { + "copyCodeResource": { + "enable": false + } + }, + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + "abiFilters": ["arm64-v8a", "x86_64"], + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/n3d/hvigorfile.ts b/n3d/hvigorfile.ts new file mode 100644 index 0000000..b0e3a1a --- /dev/null +++ b/n3d/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins: [] /* Custom plugin to extend the functionality of Hvigor. */ +} \ No newline at end of file diff --git a/n3d/obfuscation-rules.txt b/n3d/obfuscation-rules.txt new file mode 100644 index 0000000..272efb6 --- /dev/null +++ b/n3d/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/n3d/oh-package-lock.json5 b/n3d/oh-package-lock.json5 new file mode 100644 index 0000000..aa22df0 --- /dev/null +++ b/n3d/oh-package-lock.json5 @@ -0,0 +1,19 @@ +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "libn3d.so@src/main/cpp/types/libn3d": "libn3d.so@src/main/cpp/types/libn3d" + }, + "packages": { + "libn3d.so@src/main/cpp/types/libn3d": { + "name": "libn3d.so", + "version": "1.0.0", + "resolved": "", + "registryType": "local" + } + } +} \ No newline at end of file diff --git a/n3d/oh-package.json5 b/n3d/oh-package.json5 new file mode 100644 index 0000000..fe71942 --- /dev/null +++ b/n3d/oh-package.json5 @@ -0,0 +1,11 @@ +{ + "name": "n3d", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libn3d.so": "file:./src/main/cpp/types/libn3d" + } +} \ No newline at end of file diff --git a/n3d/src/main/cpp/CMakeLists.txt b/n3d/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000..46bbd08 --- /dev/null +++ b/n3d/src/main/cpp/CMakeLists.txt @@ -0,0 +1,53 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(Native3D) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +add_definitions(-DOHOS_PLATFORM) +#if(DEFINED PACKAGE_FIND_FILE) +# include(${PACKAGE_FIND_FILE}) +#endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(n3d SHARED + render/egl_core.h + render/egl_core.cpp + render/plugin_render.h + render/plugin_render.cpp + manager/plugin_manager.h + manager/plugin_manager.cpp + napi_init.cpp + common/common.h ) +find_library( + EGL-lib + EGL +) + +find_library( + GLES-lib + GLESv3 +) + +find_library( + hilog-lib + hilog_ndk.z +) + +find_library( + libace-lib + ace_ndk.z +) + +find_library( + libnapi-lib + ace_napi.z +) + +find_library( + libuv-lib + uv +) + +target_link_libraries(n3d PUBLIC ${EGL-lib} ${GLES-lib} ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_window.so) \ No newline at end of file diff --git a/n3d/src/main/cpp/common/common.h b/n3d/src/main/cpp/common/common.h new file mode 100644 index 0000000..04a5445 --- /dev/null +++ b/n3d/src/main/cpp/common/common.h @@ -0,0 +1,21 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef NATIVE3D_COMMON_H +#define NATIVE3D_COMMON_H +#include +#include +#include +#include +#include + +namespace NativeXComponentDemo { +/** + * Log print domain. + */ +const unsigned int LOG_PRINT_DOMAIN = 0xFF00; +} // namespace NativeXComponentDemo +#endif //NATIVE3D_COMMON_H diff --git a/n3d/src/main/cpp/manager/plugin_manager.cpp b/n3d/src/main/cpp/manager/plugin_manager.cpp new file mode 100644 index 0000000..280ccff --- /dev/null +++ b/n3d/src/main/cpp/manager/plugin_manager.cpp @@ -0,0 +1,220 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#include "plugin_manager.h" +#include +#include +#include +#include +#include +#include "../common/common.h" +#include + +namespace NativeXComponentDemo { +// [Start xcomponent_manager_cpp] +namespace { + // 解析从ArkTS侧传入的surfaceId,此处surfaceId是一个64位int值 + int64_t ParseId(napi_env env, napi_callback_info info) + { + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "env or info is null"); + return -1; + } + size_t argc = 1; + napi_value args[1] = {nullptr}; + if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "GetContext napi_get_cb_info failed"); + return -1; + } + int64_t value = 0; + bool lossless = true; + if (napi_ok != napi_get_value_bigint_int64(env, args[0], &value, &lossless)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "ParseId", "Get value failed"); + return -1; + } + return value; + } +} +// [StartExclude xcomponent_manager_cpp] +std::unordered_map PluginManager::pluginRenderMap_; +std::unordered_map PluginManager::windowMap_; + +PluginManager::~PluginManager() +{ + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "~PluginManager"); + for (auto iter = pluginRenderMap_.begin(); iter != pluginRenderMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + pluginRenderMap_.clear(); + for (auto iter = windowMap_.begin(); iter != windowMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + windowMap_.clear(); +} +// [EndExclude xcomponent_manager_cpp] + +PluginRender* PluginManager::GetPluginRender(int64_t& id) +{ + if (pluginRenderMap_.find(id) != pluginRenderMap_.end()) { + return pluginRenderMap_[id]; + } + return nullptr; +} + +// 设置SurfaceId,基于SurfaceId完成对NativeWindow的初始化 +napi_value PluginManager::SetSurfaceId(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + OHNativeWindow *nativeWindow; + PluginRender *pluginRender; + if (windowMap_.find(surfaceId) == windowMap_.end()) { + OH_NativeWindow_CreateNativeWindowFromSurfaceId(surfaceId, &nativeWindow); + windowMap_[surfaceId] = nativeWindow; + } else { + return nullptr; + } + if (pluginRenderMap_.find(surfaceId) == pluginRenderMap_.end()) { + pluginRender = new PluginRender(surfaceId); + pluginRenderMap_[surfaceId] = pluginRender; + } + pluginRender->InitNativeWindow(nativeWindow); + return nullptr; +} + +// 销毁Surface +napi_value PluginManager::DestroySurface(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRenderMapIter = pluginRenderMap_.find(surfaceId); + if (pluginRenderMapIter != pluginRenderMap_.end()) { + delete pluginRenderMapIter->second; + pluginRenderMap_.erase(pluginRenderMapIter); + } + auto windowMapIter = windowMap_.find(surfaceId); + if (windowMapIter != windowMap_.end()) { + OH_NativeWindow_DestroyNativeWindow(windowMapIter->second); + windowMap_.erase(windowMapIter); + } + return nullptr; +} + +// 根据传入的surfaceId、width、height实现Surface大小的变动 +napi_value PluginManager::ChangeSurface(napi_env env, napi_callback_info info) +{ + if ((env == nullptr) || (info == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "ChangeSurface: OnLoad env or info is null"); + return nullptr; + } + int64_t surfaceId = 0; + size_t argc = 3; + napi_value args[3] = {nullptr}; + + if (napi_ok != napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "ChangeSurface: GetContext napi_get_cb_info failed"); + } + bool lossless = true; + int index = 0; + if (napi_ok != napi_get_value_bigint_int64(env, args[index++], &surfaceId, &lossless)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get value failed"); + } + double width; + if (napi_ok != napi_get_value_double(env, args[index++], &width)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get width failed"); + } + double height; + if (napi_ok != napi_get_value_double(env, args[index++], &height)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get height failed"); + } + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeSurface: Get pluginRender failed"); + return nullptr; + } + pluginRender->UpdateNativeWindowSize(width, height); + return nullptr; +} + +// 实现改变绘制图形颜色的功能 +napi_value PluginManager::ChangeColor(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "ChangeColor: Get pluginRender failed"); + return nullptr; + } + pluginRender->ChangeColor(); // 参考Native XComponent场景ChangeColor实现 + return nullptr; +} + +// 实现EGL绘画逻辑 +napi_value PluginManager::DrawPattern(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", "DrawPattern: Get pluginRender failed"); + return nullptr; + } + pluginRender->DrawPattern(); + return nullptr; +} + +// 获得xcomponent状态,并返回至ArkTS侧 +napi_value PluginManager::GetXComponentStatus(napi_env env, napi_callback_info info) +{ + int64_t surfaceId = ParseId(env, info); + auto pluginRender = GetPluginRender(surfaceId); + if (pluginRender == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: Get pluginRender failed"); + return nullptr; + } + napi_value hasDraw; + napi_value hasChangeColor; + napi_status ret = napi_create_int32(env, pluginRender->HasDraw(), &(hasDraw)); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_create_int32 hasDraw_ error"); + return nullptr; + } + ret = napi_create_int32(env, pluginRender->HasChangedColor(), &(hasChangeColor)); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_create_int32 hasChangeColor_ error"); + return nullptr; + } + napi_value obj; + ret = napi_create_object(env, &obj); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, + "PluginManager", "GetXComponentStatus: napi_create_object error"); + return nullptr; + } + ret = napi_set_named_property(env, obj, "hasDraw", hasDraw); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_set_named_property hasDraw error"); + return nullptr; + } + ret = napi_set_named_property(env, obj, "hasChangeColor", hasChangeColor); + if (ret != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "PluginManager", + "GetXComponentStatus: napi_set_named_property hasChangeColor error"); + return nullptr; + } + return obj; +} +// [End xcomponent_manager_cpp] +} // namespace NativeXComponentDemo diff --git a/n3d/src/main/cpp/manager/plugin_manager.h b/n3d/src/main/cpp/manager/plugin_manager.h new file mode 100644 index 0000000..df41fd9 --- /dev/null +++ b/n3d/src/main/cpp/manager/plugin_manager.h @@ -0,0 +1,36 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef NATIVE3D_PLUGIN_MANAGER_H +#define NATIVE3D_PLUGIN_MANAGER_H + +#include +#include +#include +#include +#include +#include "../render/plugin_render.h" + +namespace NativeXComponentDemo { +// [Start xcomponent_define_class] +// PluginManager类定义 +class PluginManager { +public: + ~PluginManager(); + static PluginRender* GetPluginRender(int64_t& id); + static napi_value ChangeColor(napi_env env, napi_callback_info info); + static napi_value DrawPattern(napi_env env, napi_callback_info info); + static napi_value SetSurfaceId(napi_env env, napi_callback_info info); + static napi_value ChangeSurface(napi_env env, napi_callback_info info); + static napi_value DestroySurface(napi_env env, napi_callback_info info); + static napi_value GetXComponentStatus(napi_env env, napi_callback_info info); +public: + static std::unordered_map pluginRenderMap_; + static std::unordered_map windowMap_; +}; +// [End xcomponent_define_class] +} +#endif //NATIVE3D_PLUGIN_MANAGER_H diff --git a/n3d/src/main/cpp/napi_init.cpp b/n3d/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000..2ef49d8 --- /dev/null +++ b/n3d/src/main/cpp/napi_init.cpp @@ -0,0 +1,56 @@ +#include +#include "common/common.h" +#include "manager/plugin_manager.h" + +namespace NativeXComponentDemo { +// 在napi_init.cpp文件中,Init方法注册接口函数,从而将封装的C++方法传递出来,供ArkTS侧调用 +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + // [StartExclude xcomponent_init] + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Init", "Init begins"); + if ((env == nullptr) || (exports == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "env or exports is null"); + return nullptr; + } + // [EndExclude xcomponent_init] + // 向ArkTS侧暴露接口SetSurfaceId(),ChangeSurface(),DestroySurface(), + // DrawPattern(),ChangeColor(),GetXComponentStatus() + napi_property_descriptor desc[] = { + {"ChangeColor", nullptr, PluginManager::ChangeColor, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"SetSurfaceId", nullptr, PluginManager::SetSurfaceId, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"ChangeSurface", nullptr, PluginManager::ChangeSurface, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"GetXComponentStatus", nullptr, PluginManager::GetXComponentStatus, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"DrawPattern", nullptr, PluginManager::DrawPattern, + nullptr, nullptr, nullptr, napi_default, nullptr}, + {"DestroySurface", nullptr, PluginManager::DestroySurface, + nullptr, nullptr, nullptr, napi_default, nullptr} + }; + if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Init", "napi_define_properties failed"); + return nullptr; + } + return exports; +} +EXTERN_C_END +// 编写接口的描述信息,根据实际需要可以修改对应参数 +static napi_module n3d = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + // 入口函数 + .nm_register_func = Init, + // 模块名称 + .nm_modname = "n3d", + .nm_priv = ((void*)0), + .reserved = { 0 } }; +} // namespace NativeXComponentSample +// __attribute__((constructor))修饰的方法由系统自动调用,使用Node-API接口napi_module_register()传入模块描述信息进行模块注册 +extern "C" __attribute__((constructor)) void RegisterModule(void) +{ + napi_module_register(&NativeXComponentDemo::n3d); +} diff --git a/n3d/src/main/cpp/render/egl_core.cpp b/n3d/src/main/cpp/render/egl_core.cpp new file mode 100644 index 0000000..c7e0c3f --- /dev/null +++ b/n3d/src/main/cpp/render/egl_core.cpp @@ -0,0 +1,613 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "egl_core.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "../common/common.h" +#include "plugin_render.h" + +namespace NativeXComponentDemo { +namespace { +constexpr int32_t NUM_4 = 4; +/** + * Vertex shader. + */ +const char VERTEX_SHADER[] = "#version 300 es\n" + "layout(location = 0) in vec4 a_position;\n" + "layout(location = 1) in vec4 a_color; \n" + "out vec4 v_color; \n" + "void main() \n" + "{ \n" + " gl_Position = a_position; \n" + " v_color = a_color; \n" + "} \n"; + +/** + * Fragment shader. + */ +const char FRAGMENT_SHADER[] = "#version 300 es\n" + "precision mediump float; \n" + "in vec4 v_color; \n" + "out vec4 fragColor; \n" + "void main() \n" + "{ \n" + " fragColor = v_color; \n" + "} \n"; + +/** + * Background color #f4f4f4. + */ +const GLfloat BACKGROUND_COLOR[] = {244.0f / 255, 244.0f / 255, 244.0f / 255, 1.0f}; + +/** + * Draw color #7E8FFB. + */ +const GLfloat DRAW_COLOR[] = {126.0f / 255, 143.0f / 255, 251.0f / 255, 1.0f}; + +/** + * Change color #92D6CC. + */ +const GLfloat CHANGE_COLOR[] = {146.0f / 255, 214.0f / 255, 204.0f / 255, 1.0f}; + +/** + * Background area. + */ +const GLfloat BACKGROUND_RECTANGLE_VERTICES[] = { + -1.0f, 1.0f, + 1.0f, 1.0f, + 1.0f, -1.0f, + -1.0f, -1.0f}; + +/** + * Get context parameter count. + */ +const size_t GET_CONTEXT_PARAM_CNT = 1; + +/** + * Fifty percent. + */ +const float FIFTY_PERCENT = 0.5; + +/** + * Pointer size. + */ +const GLint POINTER_SIZE = 2; + +/** + * Triangle fan size. + */ +const GLsizei TRIANGLE_FAN_SIZE = 4; + +/** + * Egl red size default. + */ +const int EGL_RED_SIZE_DEFAULT = 8; + +/** + * Egl green size default. + */ +const int EGL_GREEN_SIZE_DEFAULT = 8; + +/** + * Egl blue size default. + */ +const int EGL_BLUE_SIZE_DEFAULT = 8; + +/** + * Egl alpha size default. + */ +const int EGL_ALPHA_SIZE_DEFAULT = 8; + +/** + * Default x position. + */ +const int DEFAULT_X_POSITION = 0; + +/** + * Default y position. + */ +const int DEFAULT_Y_POSITION = 0; + +/** + * Gl red default. + */ +const GLfloat GL_RED_DEFAULT = 0.0; + +/** + * Gl green default. + */ +const GLfloat GL_GREEN_DEFAULT = 0.0; + +/** + * Gl blue default. + */ +const GLfloat GL_BLUE_DEFAULT = 0.0; + +/** + * Gl alpha default. + */ +const GLfloat GL_ALPHA_DEFAULT = 1.0; + +/** + * Program error. + */ +const GLuint PROGRAM_ERROR = 0; + +/** + * Shape vertices size. + */ +const int SHAPE_VERTICES_SIZE = 8; + +/** + * Position handle name. + */ +const char POSITION_NAME[] = "a_position"; + +/** + * Position error. + */ +const GLint POSITION_ERROR = -1; + +/** + * Config attribute list. + */ +const EGLint ATTRIB_LIST[] = { + // Key,value. + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, EGL_RED_SIZE_DEFAULT, + EGL_GREEN_SIZE, EGL_GREEN_SIZE_DEFAULT, + EGL_BLUE_SIZE, EGL_BLUE_SIZE_DEFAULT, + EGL_ALPHA_SIZE, EGL_ALPHA_SIZE_DEFAULT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + // End. + EGL_NONE}; + +/** + * Context attributes. + */ +const EGLint CONTEXT_ATTRIBS[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE}; +} // namespace +bool EGLCore::EglContextInit(void* window) +{ + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "EglContextInit execute"); + eglWindow_ = static_cast(window); + + // Init display. + eglDisplay_ = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (eglDisplay_ == EGL_NO_DISPLAY) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglGetDisplay: unable to get EGL display"); + return false; + } + + EGLint majorVersion; + EGLint minorVersion; + if (!eglInitialize(eglDisplay_, &majorVersion, &minorVersion)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglInitialize: unable to get initialize EGL display"); + return false; + } + + // Select configuration. + const EGLint maxConfigSize = 1; + EGLint numConfigs; + if (!eglChooseConfig(eglDisplay_, ATTRIB_LIST, &eglConfig_, maxConfigSize, &numConfigs)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglChooseConfig: unable to choose configs"); + return false; + } + + return CreateEnvironment(); +} + +bool EGLCore::CreateEnvironment() +{ + // Create surface. + if (eglWindow_ == nullptr) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglWindow_ is null"); + return false; + } + eglSurface_ = eglCreateWindowSurface(eglDisplay_, eglConfig_, eglWindow_, NULL); + if (eglSurface_ == nullptr) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglCreateWindowSurface: unable to create surface"); + return false; + } + // Create context. + eglContext_ = eglCreateContext(eglDisplay_, eglConfig_, EGL_NO_CONTEXT, CONTEXT_ATTRIBS); + if (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "eglMakeCurrent failed"); + return false; + } + // Create program. + program_ = CreateProgram(VERTEX_SHADER, FRAGMENT_SHADER); + if (program_ == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "CreateProgram: unable to create program"); + return false; + } + return true; +} + +void EGLCore::Background() +{ + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background execute draw failed"); + return; + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Background FinishDraw failed"); + return; + } +} + +void EGLCore::Draw(int& hasDraw) +{ + flag_ = false; + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "Draw"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw background failed"); + return; + } + + // Divided into five quadrilaterals and calculate one of the quadrilateral's Vertices + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + // Convert DEG(54° & 18°) to RAD + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + // Convert DEG(18°) to RAD + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + // Convert DEG(18°) to RAD + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + + // Convert DEG(72°) to RAD + GLfloat rad = M_PI / 180 * 72; + // Rotate four times + for (int i = 0; i < NUM_4; ++i) { + Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); + Rotate2d(centerX, centerY, &leftX, &leftY, rad); + Rotate2d(centerX, centerY, &rightX, &rightY, rad); + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawStar(position, DRAW_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw FinishDraw failed"); + return; + } + hasDraw = 1; + + flag_ = true; +} + +void EGLCore::ChangeColor(int& hasChangeColor) +{ + if (!flag_) { + return; + } + OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor"); + GLint position = PrepareDraw(); + if (position == POSITION_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor get position failed"); + return; + } + + if (!ExecuteDraw(position, BACKGROUND_COLOR, + BACKGROUND_RECTANGLE_VERTICES, sizeof(BACKGROUND_RECTANGLE_VERTICES))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor execute draw background failed"); + return; + } + + // Divided into five quadrilaterals and calculate one of the quadrilateral's Vertices + GLfloat rotateX = 0; + GLfloat rotateY = FIFTY_PERCENT * height_; + GLfloat centerX = 0; + // Convert DEG(54° & 18°) to RAD + GLfloat centerY = -rotateY * (M_PI / 180 * 54) * (M_PI / 180 * 18); + // Convert DEG(18°) to RAD + GLfloat leftX = -rotateY * (M_PI / 180 * 18); + GLfloat leftY = 0; + // Convert DEG(18°) to RAD + GLfloat rightX = rotateY * (M_PI / 180 * 18); + GLfloat rightY = 0; + + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawNewStar(0, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + + // Convert DEG(72°) to RAD + GLfloat rad = M_PI / 180 * 72; + // Rotate four times + for (int i = 0; i < NUM_4; ++i) { + Rotate2d(centerX, centerY, &rotateX, &rotateY, rad); + Rotate2d(centerX, centerY, &leftX, &leftY, rad); + Rotate2d(centerX, centerY, &rightX, &rightY, rad); + const GLfloat shapeVertices[] = { centerX / width_, centerY / height_, leftX / width_, leftY / height_, + rotateX / width_, rotateY / height_, rightX / width_, rightY / height_ }; + + if (!ExecuteDrawNewStar(position, CHANGE_COLOR, shapeVertices, sizeof(shapeVertices))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Draw execute draw shape failed"); + return; + } + } + + if (!FinishDraw()) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ChangeColor FinishDraw failed"); + } + hasChangeColor = 1; +} + +GLint EGLCore::PrepareDraw() +{ + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (eglContext_ == nullptr) || + (!eglMakeCurrent(eglDisplay_, eglSurface_, eglSurface_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "PrepareDraw: param error"); + return POSITION_ERROR; + } + + // The gl function has no return value. + glViewport(DEFAULT_X_POSITION, DEFAULT_Y_POSITION, width_, height_); + glClearColor(GL_RED_DEFAULT, GL_GREEN_DEFAULT, GL_BLUE_DEFAULT, GL_ALPHA_DEFAULT); + glClear(GL_COLOR_BUFFER_BIT); + glUseProgram(program_); + + return glGetAttribLocation(program_, POSITION_NAME); +} + +bool EGLCore::ExecuteDraw(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glEnableVertexAttribArray(position); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + + return true; +} + +bool EGLCore::ExecuteDrawStar( + GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glVertexAttribPointer(1, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, color); + glEnableVertexAttribArray(position); + glEnableVertexAttribArray(1); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + glDisableVertexAttribArray(1); + + return true; +} + +bool EGLCore::ExecuteDrawNewStar( + GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize) +{ + if ((position > 0) || (color == nullptr) || (vertSize / sizeof(shapeVertices[0])) != SHAPE_VERTICES_SIZE) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "ExecuteDraw: param error"); + return false; + } + + // The gl function has no return value. + glVertexAttribPointer(position, POINTER_SIZE, GL_FLOAT, GL_FALSE, 0, shapeVertices); + glEnableVertexAttribArray(position); + glVertexAttrib4fv(1, color); + glDrawArrays(GL_TRIANGLE_FAN, 0, TRIANGLE_FAN_SIZE); + glDisableVertexAttribArray(position); + + return true; +} + +void EGLCore::Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat* rotateX, GLfloat* rotateY, GLfloat theta) +{ + GLfloat tempX = cos(theta) * (*rotateX - centerX) - sin(theta) * (*rotateY - centerY); + GLfloat tempY = sin(theta) * (*rotateX - centerX) + cos(theta) * (*rotateY - centerY); + *rotateX = tempX + centerX; + *rotateY = tempY + centerY; +} + +bool EGLCore::FinishDraw() +{ + // The gl function has no return value. + glFlush(); + glFinish(); + return eglSwapBuffers(eglDisplay_, eglSurface_); +} + +GLuint EGLCore::LoadShader(GLenum type, const char* shaderSrc) +{ + if ((type <= 0) || (shaderSrc == nullptr)) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader type or shaderSrc error"); + return PROGRAM_ERROR; + } + + GLuint shader = glCreateShader(type); + if (shader == 0) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCreateShader unable to load shader"); + return PROGRAM_ERROR; + } + + // The gl function has no return value. + glShaderSource(shader, 1, &shaderSrc, nullptr); + glCompileShader(shader); + + GLint compiled; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (compiled != 0) { + return shader; + } + + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen <= 1) { + glDeleteShader(shader); + return PROGRAM_ERROR; + } + + char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); + if (infoLog != nullptr) { + memset(infoLog, 0, infoLen + 1); + glGetShaderInfoLog(shader, infoLen, nullptr, infoLog); + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glCompileShader error = %s", infoLog); + free(infoLog); + infoLog = nullptr; + } + glDeleteShader(shader); + return PROGRAM_ERROR; +} + +GLuint EGLCore::CreateProgram(const char* vertexShader, const char* fragShader) +{ + if ((vertexShader == nullptr) || (fragShader == nullptr)) { + OH_LOG_Print( + LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram: vertexShader or fragShader is null"); + return PROGRAM_ERROR; + } + + GLuint vertex = LoadShader(GL_VERTEX_SHADER, vertexShader); + if (vertex == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram vertex error"); + return PROGRAM_ERROR; + } + + GLuint fragment = LoadShader(GL_FRAGMENT_SHADER, fragShader); + if (fragment == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram fragment error"); + return PROGRAM_ERROR; + } + + GLuint program = glCreateProgram(); + if (program == PROGRAM_ERROR) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram program error"); + glDeleteShader(vertex); + glDeleteShader(fragment); + return PROGRAM_ERROR; + } + + // The gl function has no return value. + glAttachShader(program, vertex); + glAttachShader(program, fragment); + glLinkProgram(program); + + GLint linked; + glGetProgramiv(program, GL_LINK_STATUS, &linked); + if (linked != 0) { + glDeleteShader(vertex); + glDeleteShader(fragment); + return program; + } + + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "createProgram linked error"); + GLint infoLen = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + char* infoLog = (char*)malloc(sizeof(char) * (infoLen + 1)); + memset(infoLog, 0, infoLen + 1); + glGetProgramInfoLog(program, infoLen, nullptr, infoLog); + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "glLinkProgram error = %s", infoLog); + free(infoLog); + infoLog = nullptr; + } + glDeleteShader(vertex); + glDeleteShader(fragment); + glDeleteProgram(program); + return PROGRAM_ERROR; +} + +void EGLCore::UpdateSize(int width, int height) +{ + width_ = width; + height_ = height; + if (width_ > 0) { + widthPercent_ = FIFTY_PERCENT * height_ / width_; + } +} + +void EGLCore::Release() +{ + if ((eglDisplay_ == nullptr) || (eglSurface_ == nullptr) || (!eglDestroySurface(eglDisplay_, eglSurface_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroySurface failed"); + } + + if ((eglDisplay_ == nullptr) || (eglContext_ == nullptr) || (!eglDestroyContext(eglDisplay_, eglContext_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglDestroyContext failed"); + } + + if ((eglDisplay_ == nullptr) || (!eglTerminate(eglDisplay_))) { + OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "EGLCore", "Release eglTerminate failed"); + } +} +} // namespace NativeXComponentDemo diff --git a/n3d/src/main/cpp/render/egl_core.h b/n3d/src/main/cpp/render/egl_core.h new file mode 100644 index 0000000..bc1f4b2 --- /dev/null +++ b/n3d/src/main/cpp/render/egl_core.h @@ -0,0 +1,50 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef NATIVE3D_EGL_CORE_H +#define NATIVE3D_EGL_CORE_H +#include +#include +#include + +namespace NativeXComponentDemo { +class EGLCore { +public: + explicit EGLCore() {}; + ~EGLCore() {} + bool EglContextInit(void* window); + bool CreateEnvironment(); + void Draw(int& hasDraw); + void Background(); + void ChangeColor(int& hasChangeColor); + void Release(); + void UpdateSize(int width, int height); + +private: + GLuint LoadShader(GLenum type, const char* shaderSrc); + GLuint CreateProgram(const char* vertexShader, const char* fragShader); + GLint PrepareDraw(); + bool ExecuteDraw(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize); + bool ExecuteDrawStar(GLint position, const GLfloat* color, const GLfloat shapeVertices[], unsigned long vertSize); + bool ExecuteDrawNewStar(GLint position, const GLfloat* color, + const GLfloat shapeVertices[], unsigned long vertSize); + void Rotate2d(GLfloat centerX, GLfloat centerY, GLfloat* rotateX, GLfloat* rotateY, GLfloat theta); + bool FinishDraw(); + +private: + EGLNativeWindowType eglWindow_; + EGLDisplay eglDisplay_ = EGL_NO_DISPLAY; + EGLConfig eglConfig_ = EGL_NO_CONFIG_KHR; + EGLSurface eglSurface_ = EGL_NO_SURFACE; + EGLContext eglContext_ = EGL_NO_CONTEXT; + GLuint program_; + bool flag_ = false; + int width_; + int height_; + GLfloat widthPercent_; +}; +} // namespace NativeXComponentDemo +#endif //NATIVE3D_EGL_CORE_H diff --git a/n3d/src/main/cpp/render/plugin_render.cpp b/n3d/src/main/cpp/render/plugin_render.cpp new file mode 100644 index 0000000..c49ad91 --- /dev/null +++ b/n3d/src/main/cpp/render/plugin_render.cpp @@ -0,0 +1,70 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "plugin_render.h" + +namespace NativeXComponentDemo { + +PluginRender::PluginRender(int64_t& id) +{ + this->id_ = id; + this->eglCore_ = new EGLCore(); + hasDraw_ = 0; + hasChangeColor_ = 0; +} +// [Start xcomponent_render_cpp] +void PluginRender::ChangeColor() +{ + eglCore_->ChangeColor(hasChangeColor_); +} + +void PluginRender::DrawPattern() +{ + eglCore_->Draw(hasDraw_); // 参考Native XComponent场景Draw实现 +} + +void PluginRender::InitNativeWindow(OHNativeWindow *window) +{ + eglCore_->EglContextInit(window); // 参考Native XComponent场景EglContextInit的实现 +} + +void PluginRender::UpdateNativeWindowSize(int width, int height) +{ + eglCore_->UpdateSize(width, height); // 参考Native XComponent场景UpdateSize的实现 + if (!hasChangeColor_ && !hasDraw_) { + eglCore_->Background(); // 参考Native XComponent场景Background的实现 + } +} + +int32_t PluginRender::HasDraw() +{ + return hasDraw_; +} + +int32_t PluginRender::HasChangedColor() +{ + return hasChangeColor_; +} +// [End xcomponent_render_cpp] +} // namespace NativeXComponentDemo + diff --git a/n3d/src/main/cpp/render/plugin_render.h b/n3d/src/main/cpp/render/plugin_render.h new file mode 100644 index 0000000..55a952d --- /dev/null +++ b/n3d/src/main/cpp/render/plugin_render.h @@ -0,0 +1,38 @@ +// +// Created on 2025/11/24. +// +// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found, +// please include "napi/native_api.h". + +#ifndef NATIVE3D_PLUGIN_RENDER_H +#define NATIVE3D_PLUGIN_RENDER_H +#include +#include +#include "egl_core.h" + +namespace NativeXComponentDemo { +class PluginRender { +public: + explicit PluginRender(int64_t& id); + ~PluginRender() + { + if (eglCore_ != nullptr) { + eglCore_->Release(); + delete eglCore_; + eglCore_ = nullptr; + } + } + void ChangeColor(); + void DrawPattern(); + int32_t HasDraw(); + int32_t HasChangedColor(); + void InitNativeWindow(OHNativeWindow* window); + void UpdateNativeWindowSize(int width, int height); +private: + EGLCore* eglCore_; + int64_t id_; + int32_t hasDraw_; + int32_t hasChangeColor_; +}; +} // namespace NativeXComponentDemo +#endif //NATIVE3D_PLUGIN_RENDER_H diff --git a/n3d/src/main/cpp/types/libn3d/Index.d.ts b/n3d/src/main/cpp/types/libn3d/Index.d.ts new file mode 100644 index 0000000..40cc7e5 --- /dev/null +++ b/n3d/src/main/cpp/types/libn3d/Index.d.ts @@ -0,0 +1,11 @@ +// 函数声明,在cpp/types/libn3d/Index.d.ts中定义 +type XComponentContextStatus = { + hasDraw: boolean, + hasChangeColor: boolean, +}; +export const SetSurfaceId: (id: BigInt) => any; +export const ChangeSurface: (id: BigInt, w: number, h: number) =>any; +export const DrawPattern: (id: BigInt) => any; +export const GetXComponentStatus: (id: BigInt) => XComponentContextStatus +export const ChangeColor: (id: BigInt) => any; +export const DestroySurface: (id: BigInt) => any; diff --git a/n3d/src/main/cpp/types/libn3d/oh-package.json5 b/n3d/src/main/cpp/types/libn3d/oh-package.json5 new file mode 100644 index 0000000..b65f486 --- /dev/null +++ b/n3d/src/main/cpp/types/libn3d/oh-package.json5 @@ -0,0 +1,6 @@ +{ + "name": "libn3d.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/n3d/src/main/ets/n3dability/N3dAbility.ets b/n3d/src/main/ets/n3dability/N3dAbility.ets new file mode 100644 index 0000000..18e9252 --- /dev/null +++ b/n3d/src/main/ets/n3dability/N3dAbility.ets @@ -0,0 +1,48 @@ +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +const DOMAIN = 0x0000; + +export default class N3dAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + try { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + } catch (err) { + hilog.error(DOMAIN, 'testTag', 'Failed to set colorMode. Cause: %{public}s', JSON.stringify(err)); + } + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); + return; + } + hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); + } +} \ No newline at end of file diff --git a/n3d/src/main/ets/n3dbackupability/N3dBackupAbility.ets b/n3d/src/main/ets/n3dbackupability/N3dBackupAbility.ets new file mode 100644 index 0000000..84da1bf --- /dev/null +++ b/n3d/src/main/ets/n3dbackupability/N3dBackupAbility.ets @@ -0,0 +1,16 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +const DOMAIN = 0x0000; + +export default class N3dBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(DOMAIN, 'testTag', 'onBackup ok'); + await Promise.resolve(); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + await Promise.resolve(); + } +} \ No newline at end of file diff --git a/n3d/src/main/ets/pages/Index.ets b/n3d/src/main/ets/pages/Index.ets new file mode 100644 index 0000000..c7fbdf7 --- /dev/null +++ b/n3d/src/main/ets/pages/Index.ets @@ -0,0 +1,100 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import Native3D from 'libn3d.so'; + +const DOMAIN = 0x0000; + +class MyXComponentController extends XComponentController{ + onSurfaceCreated(surfaceId: string): void { + console.info(`onSurfaceCreated surfaceId: ${surfaceId}`); + Native3D.SetSurfaceId(BigInt(surfaceId)); + } + onSurfaceChanged(surfaceId: string, rect: SurfaceRect): void { + console.info(`onSurfaceChanged surfaceId: ${surfaceId}, rect: ${JSON.stringify(rect)}}`); + // 在onSurfaceChanged中调用ChangeSurface绘制内容 + Native3D.ChangeSurface(BigInt(surfaceId), rect.surfaceWidth, rect.surfaceHeight); + } + onSurfaceDestroyed(surfaceId: string): void { + console.info(`onSurfaceDestroyed surfaceId: ${surfaceId}`); + Native3D.DestroySurface(BigInt(surfaceId)); + } +} + +@Entry +@Component +struct Index { + @State objStatus: string = "当前状态:"; + @State currentStatus: string = "Null"; + @State msgHead: string = 'Native3D Demo'; + @State btnRun:string ='绘制图形'; + @State btnChn:string ='改变颜色'; + ComCtrl: XComponentController = new MyXComponentController(); + build() { + Column() { + Row() { + Text(this.msgHead) + .fontSize(18) + .fontWeight(FontWeight.Bold) + }.height('10%') + + Column({ space: 10 }){ + XComponent({ + type: XComponentType.SURFACE, + controller: this.ComCtrl + }).height('80%') + + Row(){ + Text(this.objStatus) + .fontSize('24fp') + .fontWeight(500) + .height('20%') + + Text(this.currentStatus) + .fontSize('24fp') + .fontWeight(500) + .height('20%') + }.height('20%') + + }.height('80%') + + Column() { + Row(){ + Text('渲染后端:') + Checkbox() + Text('OpenGL ES') + Checkbox() + Text('Vulkan') + } + Row() { + Button(this.btnRun) + .fontSize(22) + .fontWeight(FontWeight.Bold) + .onClick(() => { + let surfaceId = this.ComCtrl.getXComponentSurfaceId(); + Native3D.DrawPattern(BigInt(surfaceId)); + let hasDraw: boolean = false; + if (Native3D.GetXComponentStatus(BigInt(surfaceId))) { + hasDraw = Native3D.GetXComponentStatus(BigInt(surfaceId)).hasDraw; + } + if (hasDraw) { + this.currentStatus = "绘制图形"; + } + }) + Button(this.btnChn) + .fontSize(22) + .fontWeight(FontWeight.Bold) + .onClick(() => { + let surfaceId = this.ComCtrl.getXComponentSurfaceId(); + Native3D.ChangeColor(BigInt(surfaceId)); + let hasChangeColor: boolean = false; + if (Native3D.GetXComponentStatus(BigInt(surfaceId))) { + hasChangeColor = Native3D.GetXComponentStatus(BigInt(surfaceId)).hasChangeColor; + } + if (hasChangeColor) { + this.currentStatus = "改变颜色"; + } + }) + } + }.height('10%') + } + } +} diff --git a/n3d/src/main/module.json5 b/n3d/src/main/module.json5 new file mode 100644 index 0000000..d9b8ca4 --- /dev/null +++ b/n3d/src/main/module.json5 @@ -0,0 +1,50 @@ +{ + "module": { + "name": "n3d", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "N3dAbility", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "N3dAbility", + "srcEntry": "./ets/n3dability/N3dAbility.ets", + "description": "$string:N3dAbility_desc", + "icon": "$media:layered_image", + "label": "$string:N3dAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "ohos.want.action.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "N3dBackupAbility", + "srcEntry": "./ets/n3dbackupability/N3dBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ], + } + ] + } +} \ No newline at end of file diff --git a/n3d/src/main/resources/base/element/color.json b/n3d/src/main/resources/base/element/color.json new file mode 100644 index 0000000..3c71296 --- /dev/null +++ b/n3d/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/n3d/src/main/resources/base/element/float.json b/n3d/src/main/resources/base/element/float.json new file mode 100644 index 0000000..33ea223 --- /dev/null +++ b/n3d/src/main/resources/base/element/float.json @@ -0,0 +1,8 @@ +{ + "float": [ + { + "name": "page_text_font_size", + "value": "50fp" + } + ] +} diff --git a/n3d/src/main/resources/base/element/string.json b/n3d/src/main/resources/base/element/string.json new file mode 100644 index 0000000..e64a632 --- /dev/null +++ b/n3d/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "N3dAbility_desc", + "value": "description" + }, + { + "name": "N3dAbility_label", + "value": "Native3D" + } + ] +} \ No newline at end of file diff --git a/n3d/src/main/resources/base/media/background.png b/n3d/src/main/resources/base/media/background.png new file mode 100644 index 0000000..923f2b3 Binary files /dev/null and b/n3d/src/main/resources/base/media/background.png differ diff --git a/n3d/src/main/resources/base/media/foreground.png b/n3d/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000..97014d3 Binary files /dev/null and b/n3d/src/main/resources/base/media/foreground.png differ diff --git a/n3d/src/main/resources/base/media/layered_image.json b/n3d/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000..fb49920 --- /dev/null +++ b/n3d/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/n3d/src/main/resources/base/media/startIcon.png b/n3d/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000..205ad8b Binary files /dev/null and b/n3d/src/main/resources/base/media/startIcon.png differ diff --git a/n3d/src/main/resources/base/profile/backup_config.json b/n3d/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000..78f40ae --- /dev/null +++ b/n3d/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/n3d/src/main/resources/base/profile/main_pages.json b/n3d/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000..1898d94 --- /dev/null +++ b/n3d/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/n3d/src/main/resources/dark/element/color.json b/n3d/src/main/resources/dark/element/color.json new file mode 100644 index 0000000..79b11c2 --- /dev/null +++ b/n3d/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/n3d/src/mock/Libn3d.mock.ets b/n3d/src/mock/Libn3d.mock.ets new file mode 100644 index 0000000..c217171 --- /dev/null +++ b/n3d/src/mock/Libn3d.mock.ets @@ -0,0 +1,7 @@ +const NativeMock: Record = { + 'add': (a: number, b: number) => { + return a + b; + }, +}; + +export default NativeMock; \ No newline at end of file diff --git a/n3d/src/mock/mock-config.json5 b/n3d/src/mock/mock-config.json5 new file mode 100644 index 0000000..abf989f --- /dev/null +++ b/n3d/src/mock/mock-config.json5 @@ -0,0 +1,5 @@ +{ + "libn3d.so": { + "source": "src/mock/Libn3d.mock.ets" + } +} \ No newline at end of file diff --git a/n3d/src/ohosTest/ets/test/Ability.test.ets b/n3d/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000..85c78f6 --- /dev/null +++ b/n3d/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,35 @@ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/n3d/src/ohosTest/ets/test/List.test.ets b/n3d/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000..794c7dc --- /dev/null +++ b/n3d/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,5 @@ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/n3d/src/ohosTest/module.json5 b/n3d/src/ohosTest/module.json5 new file mode 100644 index 0000000..c3ea67e --- /dev/null +++ b/n3d/src/ohosTest/module.json5 @@ -0,0 +1,11 @@ +{ + "module": { + "name": "n3d_test", + "type": "feature", + "deviceTypes": [ + "phone" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/n3d/src/test/List.test.ets b/n3d/src/test/List.test.ets new file mode 100644 index 0000000..bb5b5c3 --- /dev/null +++ b/n3d/src/test/List.test.ets @@ -0,0 +1,5 @@ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/n3d/src/test/LocalUnit.test.ets b/n3d/src/test/LocalUnit.test.ets new file mode 100644 index 0000000..165fc16 --- /dev/null +++ b/n3d/src/test/LocalUnit.test.ets @@ -0,0 +1,33 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/oh-package-lock.json5 b/oh-package-lock.json5 new file mode 100644 index 0000000..6b264af --- /dev/null +++ b/oh-package-lock.json5 @@ -0,0 +1,28 @@ +{ + "meta": { + "stableOrder": true, + "enableUnifiedLockfile": false + }, + "lockfileVersion": 3, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hamock@1.0.0": "@ohos/hamock@1.0.0", + "@ohos/hypium@1.0.24": "@ohos/hypium@1.0.24" + }, + "packages": { + "@ohos/hamock@1.0.0": { + "name": "", + "version": "1.0.0", + "integrity": "sha512-K6lDPYc6VkKe6ZBNQa9aoG+ZZMiwqfcR/7yAVFSUGIuOAhPvCJAo9+t1fZnpe0dBRBPxj2bxPPbKh69VuyAtDg==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hamock/-/hamock-1.0.0.har", + "registryType": "ohpm" + }, + "@ohos/hypium@1.0.24": { + "name": "", + "version": "1.0.24", + "integrity": "sha512-3dCqc+BAR5LqEGG2Vtzi8O3r7ci/3fYU+FWjwvUobbfko7DUnXGOccaror0yYuUhJfXzFK0aZNMGSnXaTwEnbw==", + "resolved": "https://ohpm.openharmony.cn/ohpm/@ohos/hypium/-/hypium-1.0.24.har", + "registryType": "ohpm" + } + } +} \ No newline at end of file diff --git a/oh-package.json5 b/oh-package.json5 new file mode 100644 index 0000000..c72aa05 --- /dev/null +++ b/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "modelVersion": "6.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.24", + "@ohos/hamock": "1.0.0" + } +}