diff --git a/ArkGraphics2D/DisplaySoloist/.gitignore b/ArkGraphics2D/DisplaySoloist/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ea27eaff8c3ecef3e9ebe6399a5f9073cd962a7b --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/.gitignore @@ -0,0 +1,13 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer +oh-package-lock.json5 \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/AppScope/app.json5 b/ArkGraphics2D/DisplaySoloist/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..feca14423759e8b0358a04fa19b0917d01b6da04 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/AppScope/app.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "bundleName": "com.samples.Displaysoloist", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/element/string.json b/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..8f27bbd6363b153e19eb343ee29e37f4a2813c8e --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "DisplaySoloist" + } + ] +} diff --git a/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/media/app_icon.png b/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/ArkGraphics2D/DisplaySoloist/AppScope/resources/base/media/app_icon.png differ diff --git a/ArkGraphics2D/DisplaySoloist/README.md b/ArkGraphics2D/DisplaySoloist/README.md new file mode 100644 index 0000000000000000000000000000000000000000..71b3f9ce2d82ceadb112876f9503c87acf1d147c --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/README.md @@ -0,0 +1,83 @@ +# DisplaySoloist分级管控 + +### 介绍 + +本示例通过 DisplaySoloist 系列功能,使用 UI 外的线程对 XComponent 的绘制内容,设置开发者所期望的帧率。使用 [NativeDisplaySoloist](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/displaysoloist-native-guidelines) 和 [Drawing](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/graphic-drawing-overview) 来实现图像绘制和显示。 + +### 效果预览 + +| XComponent | +| :------------------------------------------------------: | +| | + +使用说明 + +1.进入 XComponent 页面,依次点击“**Start**”,三个方块分别按照 30Hz、60Hz、120Hz 移动;点击“**Stop**”动画停止。 + +### 工程目录 + +``` +├──entry/src/main +│ ├──cpp // C++代码区 +│ │ ├──CMakeLists.txt // CMake配置文件 +│ │ ├──napi_init.cpp // Napi模块注册 +│ │ ├──common +│ │ │ └──log_common.h // 日志封装定义文件 +│ │ ├──plugin // 生命周期管理模块 +│ │ │ ├──plugin_manager.cpp +│ │ │ └──plugin_manager.h +│ │ ├──samples // samples渲染模块 +│ │ │ ├──sample_xcomponent.cpp +│ │ │ └──sample_xcomponent.h +│ ├──ets // ets代码区 +│ │ ├──entryability +│ │ │ ├──EntryAbility.ts // 程序入口类 +| | | └──EntryAbility.ets +| | ├──interface +│ │ │ └──XComponentContext.ts // XComponentContext +│ │ ├──pages // 页面文件 +│ │ | └──Index.ets // XComponent页面 +│ │ ├──utils // 工具类 +| ├──resources // 资源文件目录 +``` + +### 具体实现 + +* XComponent:通过在 IDE 中的 Native C++ 工程,在 TS 侧中声明对外接口为 register、unregister 以及 destroy;在 C++ 侧调用 NativeDisplaySoloist 分级管控接口,并在使用 drawing 来绘制期望帧率图像。 + + | 接口名 | 描述 | + | ------------------------------------------- | --------------------------------------------------- | + | OH_DisplaySoloist_Create | 创建一个OH_DisplaySoloist实例 | + | OH_DisplaySoloist_Destroy | 销毁一个OH_DisplaySoloist实例 | + | OH_DisplaySoloist_Start | 设置每帧回调函数,每次vsync信号到来时启动每帧回调 | + | OH_DisplaySoloist_Stop | 停止请求下一次vsync信号,并停止调用回调函数callback | + | OH_DisplaySoloist_SetExpectedFrameRateRange | 设置期望帧率范围 | + + +### 相关权限 + +不涉及。 + +### 依赖 + +不涉及。 + +### 约束与限制 + +1.本示例仅支持在标准系统上运行; + +2.本示例为 Stage 模型,已适配 API version 14 版本 SDK,SDK 版本号(API Version 14 5.0.2.57); + +3.本示例需要使用 DevEco Studio 版本号(5.0.5.306)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/DocsSample/graphic/DisplaySoloist/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/build-profile.json5 b/ArkGraphics2D/DisplaySoloist/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ee9f710ef18f2965ae1469bdb5d65f02591834f5 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/build-profile.json5 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/code-linter.json5 b/ArkGraphics2D/DisplaySoloist/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..44d50304643a42f437232b908c9b44c51cea8f60 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/code-linter.json5 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/.gitignore b/ArkGraphics2D/DisplaySoloist/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..eadab4e1522296628f32a70228b2c758ecab4759 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/.gitignore @@ -0,0 +1,7 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test +/oh-package-lock.json5 \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/build-profile.json5 b/ArkGraphics2D/DisplaySoloist/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..98128447b577770b257af752b1d5e91c850f64f4 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/build-profile.json5 @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + } + }, + "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/ArkGraphics2D/DisplaySoloist/entry/hvigorfile.ts b/ArkGraphics2D/DisplaySoloist/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..90e5d264bb75363bfb6969f0b832c2f1b9a02a75 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +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. */ +} diff --git a/ArkGraphics2D/DisplaySoloist/entry/obfuscation-rules.txt b/ArkGraphics2D/DisplaySoloist/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/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/ArkGraphics2D/DisplaySoloist/entry/oh-package.json5 b/ArkGraphics2D/DisplaySoloist/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5b21e253af246edab8b6ef4f10938f4417e8bc25 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/CMakeLists.txt b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..0e5253594a889deea00f2e32cdc8cff59ca626a7 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,23 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(drawing_test) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(entry SHARED + napi_init.cpp + samples/sample_xcomponent.cpp + plugin/plugin_manager.cpp +) +find_library( + # Sets the name of the path variable. + hilog-lib + # Specifies the name of the NDK library that + # you want CMake to locate. + hilog_ndk.z +) +target_link_libraries(entry PUBLIC ${hilog-lib}) +target_link_libraries(entry PUBLIC libace_napi.z.so libnative_drawing.so libnative_window.so libace_ndk.z.so libnative_display_soloist.so) \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/common/log_common.h b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/common/log_common.h new file mode 100644 index 0000000000000000000000000000000000000000..db232e78289ae5cddb36f10c8e98b389d19b0a47 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/common/log_common.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef LOG_COMMON_H +#define LOG_COMMON_H +#include +#define LOG_PRINT_DOMAIN 0xFF00 +#define APP_LOG_DOMAIN 0x0001 +constexpr const char *APP_LOG_TAG = "DisplaySoloistSample"; +#define SAMPLE_LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +#endif // LOG_COMMON_H diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/napi_init.cpp b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..43e51b0018c9d42e3e9831a4953e3ef1b3eee3c0 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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 "napi/native_api.h" +#include "common/log_common.h" +#include "plugin/plugin_manager.h" + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + SAMPLE_LOGI("napi init"); + PluginManager::GetInstance()->Export(env, exports); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); } diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.cpp b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b71c471ab9250ce025eba9f2f0a561960adb6c58 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2025 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 +#include +#include "common/log_common.h" +#include "plugin_manager.h" + +PluginManager *PluginManager::GetInstance() +{ + static PluginManager pluginManager; + return &pluginManager; +} + +PluginManager::~PluginManager() +{ + SAMPLE_LOGI("~PluginManager"); + for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + nativeXComponentMap_.clear(); + + for (auto iter = pluginRenderMap_.begin(); iter != pluginRenderMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + pluginRenderMap_.clear(); +} + +// [Start display_soloist_export_api] +void PluginManager::Export(napi_env env, napi_value exports) +{ + nativeXComponentMap_.clear(); + pluginRenderMap_.clear(); + if ((env == nullptr) || (exports == nullptr)) { + SAMPLE_LOGE("Export: env or exports is null"); + return; + } + + napi_value exportInstance = nullptr; + if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("Export: napi_get_named_property fail"); + return; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("Export: napi_unwrap fail"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("Export: OH_NativeXComponent_GetXComponentId fail"); + return; + } + + std::string id(idStr); + auto context = PluginManager::GetInstance(); + if ((context != nullptr) && (nativeXComponent != nullptr)) { + context->SetNativeXComponent(id, nativeXComponent); + auto render = context->GetRender(id); + if (render != nullptr) { + render->RegisterCallback(nativeXComponent); + render->Export(env, exports); + } else { + SAMPLE_LOGE("render is nullptr"); + } + } +} +// [End display_soloist_export_api] + +void PluginManager::SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent) +{ + SAMPLE_LOGI("set native xComponent, ID = %{public}s.", id.c_str()); + if (nativeXComponent == nullptr) { + SAMPLE_LOGE("xcomponent null"); + return; + } + + if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) { + nativeXComponentMap_[id] = nativeXComponent; + return; + } + + if (nativeXComponentMap_[id] != nativeXComponent) { + OH_NativeXComponent *tmp = nativeXComponentMap_[id]; + delete tmp; + tmp = nullptr; + nativeXComponentMap_[id] = nativeXComponent; + } +} + +SampleXComponent *PluginManager::GetRender(std::string &id) +{ + if (pluginRenderMap_.find(id) == pluginRenderMap_.end()) { + SampleXComponent *instance = SampleXComponent::GetInstance(id); + pluginRenderMap_[id] = instance; + return instance; + } + return pluginRenderMap_[id]; +} diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.h b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..0c323bb130ada5130718aea50a50c6c5789e3904 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/plugin/plugin_manager.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef PLUGIN_MANAGER_H +#define PLUGIN_MANAGER_H + +#include +#include +#include +#include +#include +#include "samples/sample_xcomponent.h" + +// [Start display_soloist_create_plugin_manager] +class PluginManager { +public: + ~PluginManager(); + + static PluginManager *GetInstance(); + + void SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent); + SampleXComponent *GetRender(std::string &id); + void Export(napi_env env, napi_value exports); + +private: + std::unordered_map nativeXComponentMap_; + std::unordered_map pluginRenderMap_; +}; +// [End display_soloist_create_plugin_manager] +#endif // PLUGIN_MANAGER_H diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.cpp b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..464f21b74af20dcae486375cd16e4c928a9bd56f --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2025 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 +#include +#include "common/log_common.h" +#include "sample_xcomponent.h" + +static std::unordered_map g_displaySync; + +static void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) +{ + SAMPLE_LOGI("OnSurfaceCreatedCB"); + if ((component == nullptr) || (window == nullptr)) { + SAMPLE_LOGE("OnSurfaceCreatedCB: component or window is null"); + return; + } + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("OnSurfaceCreatedCB: Unable to get XComponent id"); + return; + } + std::string id(idStr); + auto render = SampleXComponent::GetInstance(id); + OHNativeWindow *nativeWindow = static_cast(window); + render->SetNativeWindow(nativeWindow); + + uint64_t width; + uint64_t height; + int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); + if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { + render->SetHeight(height); + render->SetWidth(width); + SAMPLE_LOGI("xComponent width = %{public}llu, height = %{public}llu", width, height); + } +} + +static void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) +{ + SAMPLE_LOGI("OnSurfaceDestroyedCB"); + if ((component == nullptr) || (window == nullptr)) { + SAMPLE_LOGE("OnSurfaceDestroyedCB: component or window is null"); + return; + } + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("OnSurfaceDestroyedCB: Unable to get XComponent id"); + return; + } + std::string id(idStr); + SampleXComponent::Release(id); +} + +// [Start display_soloist_frame_rate_setting_and_subscription_function_registration] +static void TestCallback(long long timestamp, long long targetTimestamp, void *data) +{ + SAMPLE_LOGI("test callback timestamp = %{public}llu, ", timestamp); + OH_NativeXComponent *component = nullptr; + component = static_cast(data); + if (component == nullptr) { + SAMPLE_LOGE("TestCallback: component is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("TestCallback: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + auto render = SampleXComponent::GetInstance(id); + OHNativeWindow *nativeWindow = render->GetNativeWindow(); + uint64_t width; + uint64_t height; + + int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, nativeWindow, &width, &height); + if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { + render->Prepare(); + render->Create(); + if (id == "xcomponentId_30") { + int offset = 16; + render->ConstructPath(offset, offset, render->defaultOffsetY); + } + if (id == "xcomponentId_120") { + int offset = 4; + render->ConstructPath(offset, offset, render->defaultOffsetY); + } + render->SetPenAndBrush(); + render->DrawPath(); + render->DisPlay(); + render->Destroy(); + } +} +// [End display_soloist_frame_rate_setting_and_subscription_function_registration] + +static std::unordered_map g_instance; + +void SampleXComponent::SetWidth(uint64_t width) { width_ = width; } + +void SampleXComponent::SetHeight(uint64_t height) { height_ = height; } + +void SampleXComponent::SetNativeWindow(OHNativeWindow *nativeWindow) { nativeWindow_ = nativeWindow; } + +OHNativeWindow *SampleXComponent::GetNativeWindow() { return nativeWindow_; } + +void SampleXComponent::Prepare() +{ + if (nativeWindow_ == nullptr) { + SAMPLE_LOGE("nativeWindow_ is nullptr"); + return; + } + int ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer_, &fenceFd_); + SAMPLE_LOGI("request buffer ret = %{public}d", ret); + bufferHandle_ = OH_NativeWindow_GetBufferHandleFromNative(buffer_); + mappedAddr_ = static_cast( + mmap(bufferHandle_->virAddr, bufferHandle_->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle_->fd, 0)); + if (mappedAddr_ == MAP_FAILED) { + SAMPLE_LOGE("mmap failed"); + } +} + +void SampleXComponent::DisPlay() +{ + void *bitmapAddr = OH_Drawing_BitmapGetPixels(cBitmap_); + uint32_t *value = static_cast(bitmapAddr); + + uint32_t *pixel = static_cast(mappedAddr_); + if (pixel == nullptr) { + SAMPLE_LOGE("pixel is null"); + return; + } + if (value == nullptr) { + SAMPLE_LOGE("value is null"); + return; + } + for (uint32_t x = 0; x < width_; x++) { + for (uint32_t y = 0; y < height_; y++) { + *pixel++ = *value++; + } + } + Region region{nullptr, 0}; + OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer_, fenceFd_, region); + int result = munmap(mappedAddr_, bufferHandle_->size); + if (result == -1) { + SAMPLE_LOGE("munmap failed!"); + } +} + +void SampleXComponent::Create() +{ + uint32_t width = static_cast(bufferHandle_->stride / 4); + cBitmap_ = OH_Drawing_BitmapCreate(); + OH_Drawing_BitmapFormat cFormat{COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE}; + OH_Drawing_BitmapBuild(cBitmap_, width, height_, &cFormat); + + cCanvas_ = OH_Drawing_CanvasCreate(); + OH_Drawing_CanvasBind(cCanvas_, cBitmap_); + OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF)); +} + +void SampleXComponent::ConstructPath(int x, int y, int offsetY) +{ + float offsetOfAy = 100.0; + float offsetOfCy = 200.0; + + aY = offsetOfAy + offsetY; + cY = offsetOfCy + offsetY; + + if (desc) { + float offset = 1.0; + aX -= x * offset; + bX -= x * offset; + } else { + float offset = 1.0; + aX += x * offset; + bX += x * offset; + } + + if (bX >= width_) { + desc = true; + } + + if (aX <= 0) { + desc = false; + } + + float bY = aY; + float cX = bX; + float dX = aX; + float dY = cY; + + cPath_ = OH_Drawing_PathCreate(); + OH_Drawing_PathMoveTo(cPath_, aX, aY); + OH_Drawing_PathLineTo(cPath_, bX, bY); + OH_Drawing_PathLineTo(cPath_, cX, cY); + OH_Drawing_PathLineTo(cPath_, dX, dY); + + OH_Drawing_PathClose(cPath_); +} + +void SampleXComponent::SetPenAndBrush() +{ + constexpr float penWidth = 10.0f; // pen width 10 + cPen_ = OH_Drawing_PenCreate(); + OH_Drawing_PenSetAntiAlias(cPen_, true); + OH_Drawing_PenSetColor(cPen_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0x00, 0x00)); + OH_Drawing_PenSetWidth(cPen_, penWidth); + OH_Drawing_PenSetJoin(cPen_, LINE_ROUND_JOIN); + OH_Drawing_CanvasAttachPen(cCanvas_, cPen_); + + cBrush_ = OH_Drawing_BrushCreate(); + OH_Drawing_BrushSetColor(cBrush_, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0xFF, 0x00)); + + OH_Drawing_CanvasAttachBrush(cCanvas_, cBrush_); +} + +void SampleXComponent::DrawPath() +{ + OH_Drawing_CanvasDrawPath(cCanvas_, cPath_); +} + +void ExecuteDisplaySoloist(std::string id, DisplaySoloist_ExpectedRateRange range, bool useExclusiveThread, + OH_NativeXComponent *nativeXComponent) +{ + OH_DisplaySoloist *nativeDisplaySoloist = nullptr; + if (g_displaySync.find(id) == g_displaySync.end()) { + g_displaySync[id] = OH_DisplaySoloist_Create(useExclusiveThread); + } + nativeDisplaySoloist = g_displaySync[id]; + OH_DisplaySoloist_SetExpectedFrameRateRange(nativeDisplaySoloist, &range); + OH_DisplaySoloist_Start(nativeDisplaySoloist, TestCallback, nativeXComponent); +} + +// [Start display_soloist_napi_register] +napi_value SampleXComponent::NapiRegister(napi_env env, napi_callback_info info) +{ + SAMPLE_LOGI("NapiRegister"); + if ((env == nullptr) || (info == nullptr)) { + SAMPLE_LOGE("NapiRegister: env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("NapiRegister: Unable to get XComponent id"); + return nullptr; + } + SAMPLE_LOGI("RegisterID = %{public}s", idStr); + std::string id(idStr); + SampleXComponent *render = SampleXComponent().GetInstance(id); + if (render != nullptr) { + DisplaySoloist_ExpectedRateRange range; + bool useExclusiveThread = false; + if (id == "xcomponentId30") { + range = {30, 120, 30}; + } + + if (id == "xcomponentId120") { + range = {30, 120, 120}; + } + ExecuteDisplaySoloist(id, range, useExclusiveThread, nativeXComponent); + } + return nullptr; +} +// [End display_soloist_napi_register] + +napi_value SampleXComponent::NapiUnregister(napi_env env, napi_callback_info info) +{ + SAMPLE_LOGI("NapiUnregister"); + if ((env == nullptr) || (info == nullptr)) { + SAMPLE_LOGE("NapiUnregister: env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("NapiUnregister: Unable to get XComponent id"); + return nullptr; + } + SAMPLE_LOGI("ID = %{public}s", idStr); + std::string id(idStr); + SampleXComponent *render = SampleXComponent().GetInstance(id); + if (render != nullptr) { + OH_DisplaySoloist_Stop(g_displaySync[id]); + SAMPLE_LOGI("NapiUnregister executed"); + } else { + SAMPLE_LOGE("render is nullptr"); + } + return nullptr; +} + +napi_value SampleXComponent::NapiDestroy(napi_env env, napi_callback_info info) +{ + SAMPLE_LOGI("NapiUnregister"); + if ((env == nullptr) || (info == nullptr)) { + SAMPLE_LOGE("NapiDestroy: env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { + SAMPLE_LOGE("NapiDestroy: napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("NapiDestroy: napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("NapiDestroy: napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("NapiDestroy: Unable to get XComponent id"); + return nullptr; + } + SAMPLE_LOGI("ID = %{public}s", idStr); + std::string id(idStr); + SampleXComponent *render = SampleXComponent().GetInstance(id); + if (render != nullptr) { + OH_DisplaySoloist_Destroy(g_displaySync[id]); + g_displaySync.erase(id); + SAMPLE_LOGI("NapiUnregister executed"); + } else { + SAMPLE_LOGE("render is nullptr"); + } + return nullptr; +} + +SampleXComponent::~SampleXComponent() +{ + OH_Drawing_BrushDestroy(cBrush_); + cBrush_ = nullptr; + OH_Drawing_PenDestroy(cPen_); + cPen_ = nullptr; + OH_Drawing_PathDestroy(cPath_); + cPath_ = nullptr; + OH_Drawing_CanvasDestroy(cCanvas_); + cCanvas_ = nullptr; + OH_Drawing_BitmapDestroy(cBitmap_); + cBitmap_ = nullptr; + + buffer_ = nullptr; + bufferHandle_ = nullptr; + nativeWindow_ = nullptr; + mappedAddr_ = nullptr; +} + +void SampleXComponent::Destroy() +{ + OH_Drawing_BrushDestroy(cBrush_); + cBrush_ = nullptr; + OH_Drawing_PenDestroy(cPen_); + cPen_ = nullptr; + OH_Drawing_PathDestroy(cPath_); + cPath_ = nullptr; + OH_Drawing_CanvasDestroy(cCanvas_); + cCanvas_ = nullptr; + OH_Drawing_BitmapDestroy(cBitmap_); + cBitmap_ = nullptr; +} + +void SampleXComponent::Release(std::string &id) +{ + SampleXComponent *render = SampleXComponent::GetInstance(id); + if (render != nullptr) { + delete render; + render = nullptr; + g_instance.erase(g_instance.find(id)); + } +} + +void SampleXComponent::Export(napi_env env, napi_value exports) +{ + if ((env == nullptr) || (exports == nullptr)) { + SAMPLE_LOGE("Export: env or exports is null"); + return; + } + napi_property_descriptor desc[] = { + {"register", nullptr, SampleXComponent::NapiRegister, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"unregister", nullptr, SampleXComponent::NapiUnregister, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"destroy", nullptr, SampleXComponent::NapiDestroy, nullptr, nullptr, nullptr, napi_default, nullptr}}; + + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { + SAMPLE_LOGE("Export: napi_define_properties failed"); + } +} + +void SampleXComponent::RegisterCallback(OH_NativeXComponent *nativeXComponent) +{ + SAMPLE_LOGI("register callback"); + renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; + renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; + // Callback must be initialized + renderCallback_.DispatchTouchEvent = nullptr; + renderCallback_.OnSurfaceChanged = nullptr; + OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_); +} + +SampleXComponent *SampleXComponent::GetInstance(std::string &id) +{ + if (g_instance.find(id) == g_instance.end()) { + SampleXComponent *render = new SampleXComponent(id); + g_instance[id] = render; + return render; + } else { + return g_instance[id]; + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.h b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.h new file mode 100644 index 0000000000000000000000000000000000000000..9ced152ef6a1c5ad7d19ed8ac8b41d123042e26f --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/samples/sample_xcomponent.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef SAMPLE_XCOMPONENT_H +#define SAMPLE_XCOMPONENT_H +// [Start display_soloist_import_module] +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "napi/native_api.h" +// [End display_soloist_import_module] + +class SampleXComponent { +public: + SampleXComponent() = default; + ~SampleXComponent(); + explicit SampleXComponent(std::string id) : id_(id) {} + static napi_value NapiRegister(napi_env env, napi_callback_info info); + static napi_value NapiUnregister(napi_env env, napi_callback_info info); + static napi_value NapiDestroy(napi_env env, napi_callback_info info); + static void Release(std::string &id); + void DrawPath(); + void SetWidth(uint64_t width); + void SetHeight(uint64_t height); + void SetNativeWindow(OHNativeWindow *nativeWindow); + OHNativeWindow *GetNativeWindow(); + + void Prepare(); + void Create(); + void DisPlay(); + void ConstructPath(int x, int y, int offsetY); + void SetPenAndBrush(); + void Export(napi_env env, napi_value exports); + void RegisterCallback(OH_NativeXComponent *nativeXComponent); + void Destroy(); + static SampleXComponent *GetInstance(std::string &id); + std::string id_; + int defaultOffsetY = 100; + +private: + OH_NativeXComponent_Callback renderCallback_; + + uint64_t width_ = 0; + uint64_t height_ = 0; + + float aX = 0.0; + float aY = 0.0; + float bX = 80.0; + float cY = 0.0; + bool desc = false; + + OH_Drawing_Bitmap *cBitmap_ = nullptr; + OH_Drawing_Canvas *cCanvas_ = nullptr; + + OH_Drawing_Path *cPath_ = nullptr; + OH_Drawing_Brush *cBrush_ = nullptr; + OH_Drawing_Pen *cPen_ = nullptr; + OHNativeWindow *nativeWindow_ = nullptr; + uint32_t *mappedAddr_ = nullptr; + BufferHandle *bufferHandle_ = nullptr; + struct NativeWindowBuffer *buffer_ = nullptr; + int fenceFd_ = 0; +}; + +#endif diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/index.d.ts b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c14affc63e0dc8a96335ff0c4f6256d392724e2 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/index.d.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ + +export const add: (a: number, b: number) => number; \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/oh-package.json5 b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..d818828a09681b0081ebb1f0df4d8e558293a14f --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "name": "libentry.so", + "types": "./index.d.ts", + "version": "", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entryability/EntryAbility.ets b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..24e1e020777423414e599ac9b5afceb4539cd55b --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 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. + */ +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +}; diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..69a47ce6bf1d1e2d0c5a0f0432a8bb83ef1daae4 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/interface/XComponentContext.ts b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/interface/XComponentContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e24b7b9bbaca207363ac15ad3d8e8d92ab76c9b --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/interface/XComponentContext.ts @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 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. + */ +// [Start display_soloist_export_interface_xcomponent_context] +export default interface XComponentContext { + register(): void; + + unregister(): void; + + destroy(): void; +}; +// [End display_soloist_export_interface_xcomponent_context] diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/pages/Index.ets b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..9cdbd814298c46d3a75d8421f9c0730f48bf4ae1 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2025 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. + */ +// [Start display_soloist_create_xcomponent] +import XComponentContext from '../interface/XComponentContext'; +import Logger from '../utils/Logger'; + +const TAG = '[XComponentDisplaySoloist]'; + +@Entry +@Component +struct Index { + private xComponentContext1: XComponentContext | undefined = undefined; + private xComponentContext2: XComponentContext | undefined = undefined; + + // [StartExclude display_soloist_create_xcomponent] + // [Start display_soloist_disappear] + aboutToDisappear(): void { + Logger.info(TAG, 'aboutToDisappear'); + if (this.xComponentContext1) { + this.xComponentContext1.unregister(); + this.xComponentContext1.destroy(); + } + if (this.xComponentContext2) { + this.xComponentContext2.unregister(); + this.xComponentContext2.destroy(); + } + } + // [End display_soloist_disappear] + // [EndExclude display_soloist_create_xcomponent] + + build() { + Column() { + Row() { + // [StartExclude display_soloist_create_xcomponent] + Text('30fps') + .fontWeight(FontWeight.Bold) + .fontSize(12) + .fontColor(Color.Red) + .textAlign(TextAlign.End) + .width(40) + .height(30) + // [EndExclude display_soloist_create_xcomponent] + + XComponent({ + id: 'xcomponentId_30', + type: XComponentType.SURFACE, + libraryname: 'entry' + }) + .onLoad((xComponentContext) => { + this.xComponentContext1 = xComponentContext as XComponentContext; + }).width('640px') + .backgroundColor(Color.White) + }.height('40%') + + Row() { + // [StartExclude display_soloist_create_xcomponent] + Text('120fps') + .fontWeight(FontWeight.Bold) + .fontSize(12) + .fontColor(Color.Red) + .textAlign(TextAlign.End) + .width(40) + .height(30) + // [EndExclude display_soloist_create_xcomponent] + + XComponent({ + id: 'xcomponentId_120', + type: XComponentType.SURFACE, + libraryname: 'entry' + }) + .onLoad((xComponentContext) => { + this.xComponentContext2 = xComponentContext as XComponentContext; + }).width('640px') + .backgroundColor(Color.White) + }.height('40%') + // [End display_soloist_create_xcomponent] + + // [Start display_soloist_start_and_stop] + Row() { + Button('Start') + .id('Start') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 20, right: 6, left: 6 }) + .onClick(() => { + if (this.xComponentContext1) { + this.xComponentContext1.register(); + } + if (this.xComponentContext2) { + this.xComponentContext2.register(); + } + }) + .width('30%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + + Button('Stop') + .id('Stop') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 20, left: 6 }) + .onClick(() => { + if (this.xComponentContext1) { + this.xComponentContext1.unregister(); + } + if (this.xComponentContext2) { + this.xComponentContext2.unregister(); + } + }) + .width('30%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + + } + .justifyContent(FlexAlign.SpaceEvenly) + .width('100%') + .height('20%') + // [End display_soloist_start_and_stop] + } + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/utils/Logger.ets b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..009e2c38da95d7fe3f11e577ebe4d18f359d5e6c --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/ets/utils/Logger.ets @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 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. + */ +import hilog from '@ohos.hilog'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Sample_DisplaySoloist]'); \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/module.json5 b/ArkGraphics2D/DisplaySoloist/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c75d702e2d350523978ecaf801c22762ce7d40c0 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/module.json5 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/element/color.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/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/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/element/string.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7d5308ce719b744fc52c655c8d2b40fcd434cb44 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySoloist" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/background.png b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/background.png differ diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/foreground.png b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/foreground.png differ diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/layered_image.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..4f9ad6307a2bc56beb6d0fce0a49cbf213b20a74 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,6 @@ +{ + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/startIcon.png b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/media/startIcon.png differ diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/backup_config.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/main_pages.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/en_US/element/string.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..7d5308ce719b744fc52c655c8d2b40fcd434cb44 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySoloist" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/zh_CN/element/string.json b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..314e0029f8d5a580142040c3fbd9f063faa6bc83 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySoloist" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/mock/Libentry.mock.ets b/ArkGraphics2D/DisplaySoloist/entry/src/mock/Libentry.mock.ets new file mode 100644 index 0000000000000000000000000000000000000000..bbcd4ffee2dc23d8783fd8a1ac5e5325bc2b5240 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/mock/Libentry.mock.ets @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ +const nativeMock: Record = { + 'add': (a: number, b: number) => { + return a + b; + }, +}; + +export default nativeMock; \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/mock/mock-config.json5 b/ArkGraphics2D/DisplaySoloist/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..98b0ae79f0090e1fc381d54c959fb32c9f7563f4 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/mock/mock-config.json5 @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "libentry.so": { + "source": "src/mock/Libentry.mock.ets" + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/Ability.test.ets b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..300a5716521dfb56661b0e1919b15629a3af6614 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2025 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. + */ +import { beforeAll, describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import Logger from '../utils/Logger'; + +const TAG = '[Sample_DisplaySoloist]'; +const BUNDLE = 'MyApp_' +const bundleName = abilityDelegatorRegistry.getArguments().bundleName; +const DELAYMS_1S = 1000; +const DELAYMS_5S = 5000; + +export default function abilityTest() { + describe('AbilityTest', () => { + beforeAll(async (done: Function) => { + Logger.info(TAG, BUNDLE + 'StartAbility_001, begin'); + let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + try { + await abilityDelegator.startAbility({ + bundleName: bundleName, + abilityName: 'EntryAbility' + }); + } catch (err) { + Logger.info(TAG, `beforeAll exception = ${JSON.stringify(err)}`); + } + + Logger.info(TAG, BUNDLE + 'StartAbility_001, end'); + done(); + }); + + /** + * @tc.number:SUB_GRAPHIC_2D_HGM_DISPLAYSOLOIST_0100 + * @tc.name:DisplaySoloist_001 + * @tc.desc:DisplaySoloist + * @tc.size:MediumTest + * @tc.type:Function + * @tc.level:Level 1 + */ + it(BUNDLE + 'DisplaySoloist_001', 0, async (done: Function) => { + try { + Logger.info(TAG, BUNDLE + 'DisplaySoloist_001 begin'); + + let driver = Driver.create(); + await driver.delayMs(DELAYMS_1S); + + await driver.assertComponentExist(ON.id('Start')); + let startButton = await driver.findComponent(ON.id('Start')); + await startButton.click(); + await driver.delayMs(DELAYMS_5S); + + await driver.assertComponentExist(ON.id('Stop')); + let stopButton = await driver.findComponent(ON.id('Stop')); + await stopButton.click(); + await driver.delayMs(DELAYMS_1S); + + Logger.info(TAG, BUNDLE + 'DisplaySoloist_001 end'); + done(); + } catch (error) { + Logger.info(TAG, `DisplaySoloist_001 exception = ${JSON.stringify(error)}`); + expect().assertFail(); + } + }); + }) +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/List.test.ets b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..c64e0b06938d246ce044186d4b2d02b500a89e14 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/utils/Logger.ets b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..2b2a9cb5405150d8e3433101be4a449e9c15ca88 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/ets/utils/Logger.ets @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 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. + */ +import hilog from '@ohos.hilog'; + +class Logger { + private domain: number = 0xF811; + private prefix: string = ''; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xF811; + } + + debug(...args: string[]): void { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]): void { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]): void { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]): void { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Sample_DisplaySoloist]'); \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/module.json5 b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f6bdce9946cb02c0385e5a8836c133c93945013d --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/ohosTest/module.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/test/List.test.ets b/ArkGraphics2D/DisplaySoloist/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a60c87c5cbb0badf7c3fd8975034590e6fafa992 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/entry/src/test/LocalUnit.test.ets b/ArkGraphics2D/DisplaySoloist/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..b080060dcc059796d440db79c0ad94bed7b7bc79 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 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. + */ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } 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/ArkGraphics2D/DisplaySoloist/hvigor/hvigor-config.json5 b/ArkGraphics2D/DisplaySoloist/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..588ef752bc769d16dcf3826bfac354920c592813 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/hvigor/hvigor-config.json5 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | 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 */ + }, + "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/ArkGraphics2D/DisplaySoloist/hvigorfile.ts b/ArkGraphics2D/DisplaySoloist/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5e782664a81fec19737e2dff4dead3223a9b686 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +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. */ +} diff --git a/ArkGraphics2D/DisplaySoloist/oh-package.json5 b/ArkGraphics2D/DisplaySoloist/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fa0f78a6061227a18ae37179dd9fa76c6d5baa33 --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/oh-package.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/ArkGraphics2D/DisplaySoloist/ohosTest.md b/ArkGraphics2D/DisplaySoloist/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..0f0434f97e281105559249a8032a213e74af275a --- /dev/null +++ b/ArkGraphics2D/DisplaySoloist/ohosTest.md @@ -0,0 +1,13 @@ +# DisplaySoloist测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|------|--------|-------------------|------------------------------|------|------| +| 拉起应用 | 设备正常运行 | | 成功拉起应用 | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 30hz 右侧 Start | 方块按照 30hz 频率移动,同时按钮显示为 Stop | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 30hz 右侧 Stop | 方块停止移动,同时按钮显示为 Start | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 60hz 右侧 Start | 方块按照 60hz 频率移动,同时按钮显示为 Stop | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 60hz 右侧 Stop | 方块停止移动,同时按钮显示为 Start | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 120hz 右侧 Start | 方块按照 120hz 频率移动,同时按钮显示为 Stop | 是 | Pass | +| 按钮点击 | 位于主页 | 点击 120hz 右侧 Stop | 方块停止移动,同时按钮显示为 Start | 是 | Pass | \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySoloist/screenshots/device/index.png b/ArkGraphics2D/DisplaySoloist/screenshots/device/index.png new file mode 100644 index 0000000000000000000000000000000000000000..1eacc005d89abe95a1f1e3faee98196afd3e94ff Binary files /dev/null and b/ArkGraphics2D/DisplaySoloist/screenshots/device/index.png differ diff --git a/ArkGraphics2D/DisplaySync/.gitignore b/ArkGraphics2D/DisplaySync/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..08d606210b3993e8f1f4535767bb3459b7b1d208 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/.gitignore @@ -0,0 +1,13 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer +/oh-package-lock.json5 \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/AppScope/app.json5 b/ArkGraphics2D/DisplaySync/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c0dd94d4cd1681f6277d423c34d0dec9e0a10b41 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/AppScope/app.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "bundleName": "com.samples.displaysync", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/ArkGraphics2D/DisplaySync/AppScope/resources/base/element/string.json b/ArkGraphics2D/DisplaySync/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..ba13327659cfcbc5e31f01e3ab118d7c90683ef4 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "DisplaySync" + } + ] +} diff --git a/ArkGraphics2D/DisplaySync/AppScope/resources/base/media/app_icon.png b/ArkGraphics2D/DisplaySync/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a39445dc87828b76fed6d2ec470dd455c45319e3 Binary files /dev/null and b/ArkGraphics2D/DisplaySync/AppScope/resources/base/media/app_icon.png differ diff --git a/ArkGraphics2D/DisplaySync/README.md b/ArkGraphics2D/DisplaySync/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e679d025fd89e3dad4ba74f967bf7d98679c6835 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/README.md @@ -0,0 +1,112 @@ +# DisplaySync + +### 介绍 + +本示例通过 DisplaySync 系列功能,对请求动画绘制帧率、请求 UI 绘制帧率和请求自绘制内容绘制帧率设置开发者所期望的帧率。 + +本示例主要展示了 DisplaySync 系列能力,使用 [ExpectedFrameRateRange](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-explicit-animation#expectedframeraterange11) 属性来配置显性动画和属性动画的帧率;利用 [@ohos.graphics.displaySync](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-graphics-displaysync) 方法来为独立的帧率绘制、更新操作UI界面;采用 [XComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/napi-xcomponent-guidelines) 组件进行开发,从而可以请求独立的绘制帧率进行内容开发。 + +### 效果预览 + +| 初始页 | 请求动画绘制帧率 | 请求 UI 绘制帧率 | 请求自绘制内容绘制帧率 | +|:--------------------------------------------------------:|:------------------------------------------------------------:|:----------------------------------------------------:|:-------------------------------------------------------------:| +| | | | | + +使用说明 + +1.在初始页面点击“**Requesting Frame Rates for Animations**”,进入请求动画绘制帧率页面,点击“**Start**”,三个方块分别按照30Hz、40Hz、60Hz移动;点击“**Back**”返回初始页面。 + +2.在初始页面点击“**Requesting Frame Rates for UI Components**”,进入请求 UI 绘制帧率页面,点击“**Start**”,数字“**30**”按照30Hz帧率放大缩小,数字“**60**”按照60Hz帧率放大缩小;点击“**Stop**”绘制停止;点击“**Back**”返回初始页面。 + +3.在初始页面点击“**Requesting Frame Rates for Custom Content**”,进入请求自绘制内容绘制帧率页面,点击“**Start**”,两个方块分别按照30Hz、120Hz移动;点击“**Stop**”动画停止;点击“**Back**”返回初始页面。 + +### 工程目录 + +``` +├──entry/src/main +│ ├──cpp // C++代码区 +│ │ ├──CMakeLists.txt // CMake配置文件 +│ │ ├──napi_init.cpp // Napi模块注册 +│ │ ├──common +│ │ │ └──log_common.h // 日志封装定义文件 +│ │ ├──plugin // 生命周期管理模块 +│ │ │ ├──plugin_manager.cpp +│ │ │ └──plugin_manager.h +│ │ ├──samples // samples渲染模块 +│ │ │ ├──sample_xcomponent.cpp +│ │ │ └──sample_xcomponent.h +│ ├──ets // ets代码区 +│ │ ├──entryability +│ │ │ ├──EntryAbility.ts // 程序入口类 +| | | └──EntryAbility.ets +| | ├──interface +│ │ │ └──XComponentContext.ts // XComponentContext +│ │ ├──DispalySync // 业务页面目录 +│ │ | ├──PropertyAnimationDisplaySync.ets // 属性动画页面 +│ │ | ├──CustomDrawDisplaySync.ets // 自绘制页面 +│ │ | ├──XComponentDisplaySync.ets // XComponent页面 +│ │ ├──pages // 页面文件 +│ │ | ├──Index.ets // 初始页面 +│ │ ├──utils // 工具类 +| ├──resources // 资源文件目录 +``` + +### 具体实现 + +* 请求动画绘制帧率:通过 animation 和 animateTo 接口的参数——expectedFrameRateRange,来设置动画刷新率区间、预期刷新率。其作为动画的属性之一,主要作用于动画场景。 + + * 涉及到的相关接口: + + | 新增动画属性 | 描述 | + | :--------------------: | :----------------------------------------------------------: | + | expectedFrameRateRange | animation和animateTo接口的动画属性参数中可选配置expectedFrameRateRange参数 | +* 请求 UI 绘制帧率:通过调用 [@ohos.graphics.displaySync ](https://docs.openharmony.cn/pages/v4.1/zh-cn/application-dev/reference/apis-arkgraphics2d/js-apis-graphics-displaySync.md)接口,来注册回调和设置刷新区间并控制回调周期。 + + * 涉及到的相关接口: + + 通过 `import displaySync from '@ohos.graphics.displaySync` 表达式引入, + + | 接口名 | 描述 | + | ------------------------------------------------------------ | -------------------------- | + | Create(): DisplaySync | 创建一个DisplaySync实例 | + | setExpectedFrameRateRange(rateRange: ExpectedFrameRateRange): void | 设置期望帧率 | + | on(type: 'frame', callback: Callback): void | 设置自定义绘制内容回调函数 | + | off(type: 'frame', callback?: Callback): void | 清除自定义绘制内容回调函数 | + | start(): void | DisplaySync使能 | + | stop(): void | DisplaySync失能 | + +* 请求自绘制内容绘制帧率:通过在 IDE 中的 Native c++ 工程,在 c++ 代码中定义对外接口为 register 和 unregister,并调用新增的 CAPI 接口,可在页面上使用 drawing 根据设定的期望帧率来绘制。 + + | 接口名 | 描述 | + | --------------------------------------------- | ---------------- | + | OH_NativeXComponent_SetExpectedFrameRateRange | 设置期望帧率范围 | + | OH_NativeXComponent_RegisterOnFrameCallback | 注册更新回调 | + | OH_NativeXComponent_UnregisterOnFrameCallback | 取消更新回调 | + +### 相关权限 + +不涉及。 + +### 依赖 + +不涉及。 + +### 约束与限制 + +1.本示例仅支持在标准系统上运行; + +2.本示例为 Stage 模型,已适配 API version 14 版本 SDK,SDK 版本号(API Version 14 5.0.2.57); + +3.本示例需要使用 DevEco Studio 版本号(5.0.5.306)及以上版本才可编译运行。 + +### 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/DocsSample/graphic/DisplaySync/ > .git/info/sparse-checkout +git remote add origin https://gitee.com/openharmony/applications_app_samples.git +git pull origin master +``` \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/build-profile.json5 b/ArkGraphics2D/DisplaySync/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..ee9f710ef18f2965ae1469bdb5d65f02591834f5 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/build-profile.json5 @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "app": { + "signingConfigs": [], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compatibleSdkVersion": "5.0.2(14)", + "targetSdkVersion": "5.0.2(14)", + "runtimeOS": "HarmonyOS" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/code-linter.json5 b/ArkGraphics2D/DisplaySync/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..44d50304643a42f437232b908c9b44c51cea8f60 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/code-linter.json5 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/.gitignore b/ArkGraphics2D/DisplaySync/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/build-profile.json5 b/ArkGraphics2D/DisplaySync/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fe58975fb74c7156ddd3d6fe75cd044005b1bb24 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/build-profile.json5 @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/hvigorfile.ts b/ArkGraphics2D/DisplaySync/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..90e5d264bb75363bfb6969f0b832c2f1b9a02a75 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +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. */ +} diff --git a/ArkGraphics2D/DisplaySync/entry/obfuscation-rules.txt b/ArkGraphics2D/DisplaySync/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/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/ArkGraphics2D/DisplaySync/entry/oh-package.json5 b/ArkGraphics2D/DisplaySync/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..10cda399b0aec3099b257299a57d284393e4e55a --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/oh-package.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": {} +} + diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/CMakeLists.txt b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd73ec36d8c9197555560566981324534d12a49a --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.4.1) +project(drawing_test) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include) + +add_library(entry SHARED + napi_init.cpp + samples/sample_xcomponent.cpp + plugin/plugin_manager.cpp +) +find_library( + hilog-lib + hilog_ndk.z +) +target_link_libraries(entry PUBLIC ${hilog-lib}) +target_link_libraries(entry PUBLIC libace_napi.z.so libnative_drawing.so libnative_window.so libace_ndk.z.so) \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/common/log_common.h b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/common/log_common.h new file mode 100644 index 0000000000000000000000000000000000000000..be73dc024b8b89d65234ebd5954caa9fd100a510 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/common/log_common.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef LOG_COMMON_H +#define LOG_COMMON_H +#include +#define LOG_PRINT_DOMAIN 0xFF00 +#define APP_LOG_DOMAIN 0x0001 +constexpr const char *APP_LOG_TAG = "DisplaySyncSample"; +#define SAMPLE_LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) +#define SAMPLE_LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, APP_LOG_TAG, __VA_ARGS__)) + +#endif // LOG_COMMON_H diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/napi_init.cpp b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e83edffd520caef11d630247c4c0f23a9180fac4 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 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 "napi/native_api.h" +#include "plugin/plugin_manager.h" + +#undef LOG_DOMIN +#undef LOG_TAG +#define LOG_DOMIN 0X0000 +#define LOG_TAG "NAPI_INIT" + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) +{ + PluginManager::GetInstance()->Export(env, exports); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterEntryModule(void) +{ + napi_module_register(&demoModule); +} diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.cpp b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31a6582ecf81446448441f7b319d7a55b5056b58 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025 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 +#include +#include "common/log_common.h" +#include "plugin_manager.h" + +PluginManager *PluginManager::GetInstance() +{ + static PluginManager pluginManager; + return &pluginManager; +} + +PluginManager::~PluginManager() +{ + SAMPLE_LOGI("~PluginManager"); + for (auto iter = nativeXComponentMap_.begin(); iter != nativeXComponentMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + nativeXComponentMap_.clear(); + + for (auto iter = pluginRenderMap_.begin(); iter != pluginRenderMap_.end(); ++iter) { + if (iter->second != nullptr) { + delete iter->second; + iter->second = nullptr; + } + } + pluginRenderMap_.clear(); +} + +void PluginManager::Export(napi_env env, napi_value exports) +{ + nativeXComponentMap_.clear(); + pluginRenderMap_.clear(); + if ((env == nullptr) || (exports == nullptr)) { + SAMPLE_LOGE("Export: env or exports is null"); + return; + } + + napi_value exportInstance = nullptr; + if (napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("Export: napi_get_named_property fail"); + return; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("Export: napi_unwrap fail"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("Export: OH_NativeXComponent_GetXComponentId fail"); + return; + } + + std::string id(idStr); + auto context = PluginManager::GetInstance(); + if ((context != nullptr) && (nativeXComponent != nullptr)) { + context->SetNativeXComponent(id, nativeXComponent); + auto render = context->GetRender(id); + if (render != nullptr) { + render->RegisterCallback(nativeXComponent); + render->Export(env, exports); + } else { + SAMPLE_LOGE("render is nullptr"); + } + } +} + +void PluginManager::SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent) +{ + SAMPLE_LOGI("set native xComponent, ID = %{public}s.", id.c_str()); + if (nativeXComponent == nullptr) { + SAMPLE_LOGE("xcomponent null"); + return; + } + + if (nativeXComponentMap_.find(id) == nativeXComponentMap_.end()) { + nativeXComponentMap_[id] = nativeXComponent; + return; + } + + if (nativeXComponentMap_[id] != nativeXComponent) { + OH_NativeXComponent *tmp = nativeXComponentMap_[id]; + delete tmp; + tmp = nullptr; + nativeXComponentMap_[id] = nativeXComponent; + } +} + +SampleXComponent *PluginManager::GetRender(std::string &id) +{ + if (pluginRenderMap_.find(id) == pluginRenderMap_.end()) { + SampleXComponent *instance = SampleXComponent::GetInstance(id); + pluginRenderMap_[id] = instance; + return instance; + } + return pluginRenderMap_[id]; +} + diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.h b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..96242845453b1fffdfea4b24acb5cdcd47520af1 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/plugin/plugin_manager.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef PLUGIN_MANAGER_H +#define PLUGIN_MANAGER_H + +#include +#include +#include +#include +#include +#include "samples/sample_xcomponent.h" + +class PluginManager { +public: + ~PluginManager(); + + static PluginManager *GetInstance(); + + void SetNativeXComponent(std::string &id, OH_NativeXComponent *nativeXComponent); + SampleXComponent *GetRender(std::string &id); + void Export(napi_env env, napi_value exports); + +private: + std::unordered_map nativeXComponentMap_; + std::unordered_map pluginRenderMap_; +}; +#endif // PLUGIN_MANAGER_H diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.cpp b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59429267f32b306f6067b17999be21aaa0e8fc1c --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.cpp @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2025 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 +#include "common/log_common.h" +#include "sample_xcomponent.h" + +static void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) +{ + SAMPLE_LOGI("OnSurfaceCreatedCB"); + if ((component == nullptr) || (window == nullptr)) { + SAMPLE_LOGE("OnSurfaceCreatedCB: component or window is null"); + return; + } + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("OnSurfaceCreatedCB: Unable to get XComponent id"); + return; + } + std::string id(idStr); + auto render = SampleXComponent::GetInstance(id); + OHNativeWindow *nativeWindow = static_cast(window); + render->SetNativeWindow(nativeWindow); + + uint64_t width; + uint64_t height; + int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, window, &width, &height); + if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { + render->SetHeight(height); + render->SetWidth(width); + SAMPLE_LOGI("xComponent width = %{public}lu, height = %{public}llu", width, height); + } +} + +static void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) +{ + SAMPLE_LOGI("OnSurfaceDestroyedCB"); + if ((component == nullptr) || (window == nullptr)) { + SAMPLE_LOGE("OnSurfaceDestroyedCB: component or window is null"); + return; + } + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("OnSurfaceDestroyedCB: Unable to get XComponent id"); + return; + } + std::string id(idStr); + SampleXComponent::Release(id); +} + +// Start display_sync_napi_frame_rate_setting_and_subscription_function_registration] +static void TestCallback(OH_NativeXComponent *component, uint64_t timestamp, uint64_t targetTimestamp) +{ + SAMPLE_LOGI("test callback timestamp = %{public}llu, targetTimestamp = %{public}llu", timestamp, targetTimestamp); + if (component == nullptr) { + SAMPLE_LOGE("TestCallback: component is null"); + return; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("TestCallback: Unable to get XComponent id"); + return; + } + + std::string id(idStr); + auto render = SampleXComponent::GetInstance(id); + OHNativeWindow *nativeWindow = render->GetNativeWindow(); + uint64_t width; + uint64_t height; + + int32_t xSize = OH_NativeXComponent_GetXComponentSize(component, nativeWindow, &width, &height); + if ((xSize == OH_NATIVEXCOMPONENT_RESULT_SUCCESS) && (render != nullptr)) { + render->Prepare(); + render->Create(); + if (id == "xcomponentId_30") { + int offset = 16; + render->ConstructPath(offset, offset, render->defaultOffsetY); + } + if (id == "xcomponentId_120") { + int offset = 4; + render->ConstructPath(offset, offset, render->defaultOffsetY); + } + render->SetPenAndBrush(); + render->DrawPath(); + render->DisPlay(); + render->Destroy(); + } +} +// End display_sync_napi_frame_rate_setting_and_subscription_function_registration] + +static std::unordered_map g_instance; + +void SampleXComponent::SetWidth(uint64_t width) +{ + width_ = width; +} + +void SampleXComponent::SetHeight(uint64_t height) +{ + height_ = height; +} + +void SampleXComponent::SetNativeWindow(OHNativeWindow *nativeWindow) +{ + nativeWindow_ = nativeWindow; +} + +OHNativeWindow *SampleXComponent::GetNativeWindow() +{ + return nativeWindow_; +} + +void SampleXComponent::Prepare() +{ + if (nativeWindow_ == nullptr) { + SAMPLE_LOGE("nativeWindow_ is nullptr"); + return; + } + int ret = OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow_, &buffer_, &fenceFd_); + SAMPLE_LOGI("request buffer ret = %{public}d", ret); + bufferHandle_ = OH_NativeWindow_GetBufferHandleFromNative(buffer_); + mappedAddr_ = static_cast( + mmap(bufferHandle_->virAddr, bufferHandle_->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle_->fd, 0)); + if (mappedAddr_ == MAP_FAILED) { + SAMPLE_LOGE("mmap failed"); + } +} + +void SampleXComponent::DisPlay() +{ + void *bitmapAddr = OH_Drawing_BitmapGetPixels(cBitmap_); + uint32_t *value = static_cast(bitmapAddr); + + uint32_t *pixel = static_cast(mappedAddr_); + if (pixel == nullptr) { + SAMPLE_LOGE("pixel is null"); + return; + } + if (value == nullptr) { + SAMPLE_LOGE("value is null"); + return; + } + for (uint32_t x = 0; x < width_; x++) { + for (uint32_t y = 0; y < height_; y++) { + *pixel++ = *value++; + } + } + Region region {nullptr, 0}; + OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow_, buffer_, fenceFd_, region); + int result = munmap(mappedAddr_, bufferHandle_->size); + if (result == -1) { + SAMPLE_LOGE("munmap failed!"); + } +} + +void SampleXComponent::Create() +{ + uint32_t width = static_cast(bufferHandle_->stride / 4); + cBitmap_ = OH_Drawing_BitmapCreate(); + OH_Drawing_BitmapFormat cFormat {COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_OPAQUE}; + OH_Drawing_BitmapBuild(cBitmap_, width, height_, &cFormat); + + cCanvas_ = OH_Drawing_CanvasCreate(); + OH_Drawing_CanvasBind(cCanvas_, cBitmap_); + OH_Drawing_CanvasClear(cCanvas_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0xFF, 0xFF)); +} + +void SampleXComponent::ConstructPath(int x, int y, int offsetY) +{ + float offsetOfAy = 100.0; + float offsetOfCy = 200.0; + float offsetOfX = 1.0; + + aY = offsetOfAy + offsetY; + cY = offsetOfCy + offsetY; + + if (desc) { + aX -= x * offsetOfX; + bX -= x * offsetOfX; + } else { + aX += x * offsetOfX; + bX += x * offsetOfX; + } + + if (bX >= width_) { + desc = true; + } + + if (aX <= 0) { + desc = false; + } + + float bY = aY; + float cX = bX; + float dX = aX; + float dY = cY; + + cPath_ = OH_Drawing_PathCreate(); + OH_Drawing_PathMoveTo(cPath_, aX, aY); + OH_Drawing_PathLineTo(cPath_, bX, bY); + OH_Drawing_PathLineTo(cPath_, cX, cY); + OH_Drawing_PathLineTo(cPath_, dX, dY); + + OH_Drawing_PathClose(cPath_); +} + +void SampleXComponent::SetPenAndBrush() +{ + constexpr float penWidth = 10.0f; + cPen_ = OH_Drawing_PenCreate(); + OH_Drawing_PenSetAntiAlias(cPen_, true); + OH_Drawing_PenSetColor(cPen_, OH_Drawing_ColorSetArgb(0xFF, 0xFF, 0x00, 0x00)); + OH_Drawing_PenSetWidth(cPen_, penWidth); + OH_Drawing_PenSetJoin(cPen_, LINE_ROUND_JOIN); + OH_Drawing_CanvasAttachPen(cCanvas_, cPen_); + + cBrush_ = OH_Drawing_BrushCreate(); + OH_Drawing_BrushSetColor(cBrush_, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0xFF, 0x00)); + + OH_Drawing_CanvasAttachBrush(cCanvas_, cBrush_); +} + +void SampleXComponent::DrawPath() +{ + OH_Drawing_CanvasDrawPath(cCanvas_, cPath_); +} + +// [Start display_sync_napi_register] +napi_value SampleXComponent::NapiRegister(napi_env env, napi_callback_info info) +{ + // [StartExclude display_sync_napi_register] + SAMPLE_LOGI("NapiRegister"); + if ((env == nullptr) || (info == nullptr)) { + SAMPLE_LOGE("NapiRegister: env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("NapiRegister: napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("NapiRegister: Unable to get XComponent id"); + return nullptr; + } + SAMPLE_LOGI("RegisterID = %{public}s", idStr); + std::string id(idStr); + SampleXComponent *render = SampleXComponent().GetInstance(id); + if (render != nullptr) { + if (id == "xcomponentId_30") { + OH_NativeXComponent_ExpectedRateRange range = {30, 120, 30}; + OH_NativeXComponent_SetExpectedFrameRateRange(nativeXComponent, &range); + } + + if (id == "xcomponentId_120") { + OH_NativeXComponent_ExpectedRateRange range = {30, 120, 120}; + OH_NativeXComponent_SetExpectedFrameRateRange(nativeXComponent, &range); + } + // [EndExclude display_sync_napi_register] + render->RegisterOnFrameCallback(nativeXComponent); + // [StartExclude display_sync_napi_register] + } + return nullptr; + // [EndExclude display_sync_napi_register] +} +// [End display_sync_napi_register] + +// [Start display_sync_napi_unregister] +napi_value SampleXComponent::NapiUnregister(napi_env env, napi_callback_info info) +{ + // [StartExclude display_sync_napi_unregister] + SAMPLE_LOGI("NapiUnregister"); + if ((env == nullptr) || (info == nullptr)) { + SAMPLE_LOGE("NapiUnregister: env or info is null"); + return nullptr; + } + + napi_value thisArg; + if (napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, nullptr) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_get_cb_info fail"); + return nullptr; + } + + napi_value exportInstance; + if (napi_get_named_property(env, thisArg, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_get_named_property fail"); + return nullptr; + } + + OH_NativeXComponent *nativeXComponent = nullptr; + if (napi_unwrap(env, exportInstance, reinterpret_cast(&nativeXComponent)) != napi_ok) { + SAMPLE_LOGE("NapiUnregister: napi_unwrap fail"); + return nullptr; + } + + char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; + uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; + if (OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) { + SAMPLE_LOGE("NapiUnregister: Unable to get XComponent id"); + return nullptr; + } + SAMPLE_LOGI("ID = %{public}s", idStr); + std::string id(idStr); + SampleXComponent *render = SampleXComponent().GetInstance(id); + if (render != nullptr) { + // [EndExclude display_sync_napi_unregister] + OH_NativeXComponent_UnregisterOnFrameCallback(nativeXComponent); + // [StartExclude display_sync_napi_unregister] + SAMPLE_LOGI("NapiUnregister executed"); + } else { + SAMPLE_LOGE("render is nullptr"); + } + return nullptr; + // [EndExclude display_sync_napi_unregister] +} +// [End display_sync_napi_unregister] + +SampleXComponent::~SampleXComponent() +{ + OH_Drawing_BrushDestroy(cBrush_); + cBrush_ = nullptr; + OH_Drawing_PenDestroy(cPen_); + cPen_ = nullptr; + OH_Drawing_PathDestroy(cPath_); + cPath_ = nullptr; + OH_Drawing_CanvasDestroy(cCanvas_); + cCanvas_ = nullptr; + OH_Drawing_BitmapDestroy(cBitmap_); + cBitmap_ = nullptr; + + buffer_ = nullptr; + bufferHandle_ = nullptr; + nativeWindow_ = nullptr; + mappedAddr_ = nullptr; +} + +void SampleXComponent::Destroy() +{ + OH_Drawing_BrushDestroy(cBrush_); + cBrush_ = nullptr; + OH_Drawing_PenDestroy(cPen_); + cPen_ = nullptr; + OH_Drawing_PathDestroy(cPath_); + cPath_ = nullptr; + OH_Drawing_CanvasDestroy(cCanvas_); + cCanvas_ = nullptr; + OH_Drawing_BitmapDestroy(cBitmap_); + cBitmap_ = nullptr; +} + +void SampleXComponent::Release(std::string &id) +{ + SampleXComponent *render = SampleXComponent::GetInstance(id); + if (render != nullptr) { + delete render; + render = nullptr; + g_instance.erase(g_instance.find(id)); + } +} + +void SampleXComponent::Export(napi_env env, napi_value exports) +{ + if ((env == nullptr) || (exports == nullptr)) { + SAMPLE_LOGE("Export: env or exports is null"); + return; + } + napi_property_descriptor desc[] = { + {"register", nullptr, SampleXComponent::NapiRegister, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"unregister", nullptr, SampleXComponent::NapiUnregister, nullptr, nullptr, nullptr, napi_default, nullptr}}; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + if (napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc) != napi_ok) { + SAMPLE_LOGE("Export: napi_define_properties failed"); + } +} + +void SampleXComponent::RegisterCallback(OH_NativeXComponent *nativeXComponent) +{ + SAMPLE_LOGI("register callback"); + renderCallback_.OnSurfaceCreated = OnSurfaceCreatedCB; + renderCallback_.OnSurfaceDestroyed = OnSurfaceDestroyedCB; + renderCallback_.DispatchTouchEvent = nullptr; + renderCallback_.OnSurfaceChanged = nullptr; + OH_NativeXComponent_RegisterCallback(nativeXComponent, &renderCallback_); +} + +// [Start display_sync_register_on_frame_callback] +void SampleXComponent::RegisterOnFrameCallback(OH_NativeXComponent *nativeXComponent) +{ + SAMPLE_LOGI("register onFrameCallback"); + OH_NativeXComponent_RegisterOnFrameCallback(nativeXComponent, TestCallback); +} +// [End display_sync_register_on_frame_callback] + +SampleXComponent *SampleXComponent::GetInstance(std::string &id) +{ + if (g_instance.find(id) == g_instance.end()) { + SampleXComponent *render = new SampleXComponent(id); + g_instance[id] = render; + return render; + } else { + return g_instance[id]; + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.h b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.h new file mode 100644 index 0000000000000000000000000000000000000000..4cf365a9fc5da0e01ed5900e13f598b0bf0d09a3 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/samples/sample_xcomponent.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025 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. + */ +#ifndef SAMPLE_XCOMPONENT_H +#define SAMPLE_XCOMPONENT_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "napi/native_api.h" + +class SampleXComponent { +public: + SampleXComponent() = default; + ~SampleXComponent(); + explicit SampleXComponent(std::string id) : id_(id) {} + static napi_value NapiRegister(napi_env env, napi_callback_info info); + static napi_value NapiUnregister(napi_env env, napi_callback_info info); + static void Release(std::string &id); + void DrawPath(); + void SetWidth(uint64_t width); + void SetHeight(uint64_t height); + void SetNativeWindow(OHNativeWindow *nativeWindow); + OHNativeWindow *GetNativeWindow(); + + void Prepare(); + void Create(); + void DisPlay(); + void ConstructPath(int x, int y, int offsetY); + void SetPenAndBrush(); + void Export(napi_env env, napi_value exports); + void RegisterCallback(OH_NativeXComponent *nativeXComponent); + void RegisterOnFrameCallback(OH_NativeXComponent *nativeXComponent); + void Destroy(); + static SampleXComponent *GetInstance(std::string &id); + std::string id_; + int defaultOffsetY = 200; + +private: + OH_NativeXComponent_Callback renderCallback_; + + uint64_t width_ = 0; + uint64_t height_ = 0; + + float aX = 0.0; + float aY = 0.0; + float bX = 80.0; + float cY = 0.0; + bool desc = false; + + OH_Drawing_Bitmap *cBitmap_ = nullptr; + OH_Drawing_Canvas *cCanvas_ = nullptr; + + OH_Drawing_Path *cPath_ = nullptr; + OH_Drawing_Brush *cBrush_ = nullptr; + OH_Drawing_Pen *cPen_ = nullptr; + OHNativeWindow *nativeWindow_ = nullptr; + uint32_t *mappedAddr_ = nullptr; + BufferHandle *bufferHandle_ = nullptr; + struct NativeWindowBuffer *buffer_ = nullptr; + int fenceFd_ = 0; +}; + +#endif diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/index.d.ts b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..5c14affc63e0dc8a96335ff0c4f6256d392724e2 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/index.d.ts @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ + +export const add: (a: number, b: number) => number; \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/oh-package.json5 b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..77052c679533e45a066030a9fc21cdbf9cbcf995 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/CustomDrawDisplaySync.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/CustomDrawDisplaySync.ets new file mode 100644 index 0000000000000000000000000000000000000000..9368d12fdeab8e5555baaba23a8bc90ae398fce6 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/CustomDrawDisplaySync.ets @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2025 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. + */ +// [Start display_sync_by_ui_import_module] +import { displaySync } from '@kit.ArkGraphics2D'; +// [End display_sync_by_ui_import_module] +import { router } from '@kit.ArkUI'; + +// [Start display_sync_by_ui_complete_example] +// [Start display_sync_create_object] +@Entry +@Component +struct Index { + // [StartExclude display_sync_create_object] + @State drawFirstSize: number = 25; + @State drawSecondSize: number = 25; + // [EndExclude display_sync_create_object] + private backDisplaySyncSlow: displaySync.DisplaySync | undefined = undefined; + private backDisplaySyncFast: displaySync.DisplaySync | undefined = undefined; +// [End display_sync_create_object] + private isBigger_30: boolean = true; + private isBigger_60: boolean = true; + + // [Start display_sync_create_text_component] + @Builder + doSomeRenderFirst() { + Text('30') + .fontSize(this.drawFirstSize) + } + + @Builder + doSomeRenderSecond() { + Text('60') + .fontSize(this.drawSecondSize) + } + // [End display_sync_create_text_component] + + // [Start display_sync_frame_rate_setting_and_subscription_function_registration] + CreateDisplaySyncSlow() { + let range: ExpectedFrameRateRange = { + expected: 30, + min: 0, + max: 120 + }; + + let draw30 = (intervalInfo: displaySync.IntervalInfo) => { + if (this.isBigger_30) { + this.drawFirstSize += 1; + if (this.drawFirstSize > 150) { + this.isBigger_30 = false; + } + } else { + this.drawFirstSize -= 1; + if (this.drawFirstSize < 25) { + this.isBigger_30 = true; + } + } + }; + + this.backDisplaySyncSlow = displaySync.create(); + this.backDisplaySyncSlow.setExpectedFrameRateRange(range); + this.backDisplaySyncSlow.on("frame", draw30); + } + // [End display_sync_frame_rate_setting_and_subscription_function_registration] + + CreateDisplaySyncFast() { + let range: ExpectedFrameRateRange = { + expected: 60, + min: 0, + max: 120 + }; + + let draw60 = (intervalInfo: displaySync.IntervalInfo) => { + if (this.isBigger_60) { + this.drawSecondSize += 1; + if (this.drawSecondSize > 150) { + this.isBigger_60 = false; + } + } else { + this.drawSecondSize -= 1; + if (this.drawSecondSize < 25) { + this.isBigger_60 = true; + } + } + }; + + this.backDisplaySyncFast = displaySync.create(); + this.backDisplaySyncFast.setExpectedFrameRateRange(range); + this.backDisplaySyncFast.on("frame", draw60); + } + + // [Start display_sync_call_stop] + aboutToDisappear() { + if (this.backDisplaySyncSlow) { + this.backDisplaySyncSlow.stop(); + this.backDisplaySyncSlow = undefined; + } + if (this.backDisplaySyncFast) { + this.backDisplaySyncFast.stop(); + this.backDisplaySyncFast = undefined; + } + } + // [End display_sync_call_stop] + + build() { + Column() { + Row() { + this.doSomeRenderFirst(); + } + .height('40%') + + Row() { + this.doSomeRenderSecond(); + } + .height('40%') + + Row() { + // [Start display_sync_start_per_frame_callback] + Button('Start') + .id('CustomDrawStart') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 10, left: 5 }) + .fontColor(Color.White) + .onClick((): void => { + if (this.backDisplaySyncSlow == undefined) { + this.CreateDisplaySyncSlow(); + } + if (this.backDisplaySyncFast == undefined) { + this.CreateDisplaySyncFast(); + } + if (this.backDisplaySyncSlow) { + this.backDisplaySyncSlow.start(); + } + if (this.backDisplaySyncFast) { + this.backDisplaySyncFast.start(); + } + }) + .width('20%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + // [End display_sync_start_per_frame_callback] + + // [Start display_sync_stop_per_frame_callback] + Button('Stop') + .id('CustomDrawStop') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 10, left: 5 }) + .fontColor(Color.White) + .onClick((): void => { + if (this.backDisplaySyncSlow) { + this.backDisplaySyncSlow.stop(); + } + if (this.backDisplaySyncFast) { + this.backDisplaySyncFast.stop(); + } + }) + .width('20%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + // [End display_sync_stop_per_frame_callback] + + Button('Back') + .id('CustomDrawBack') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 10, left: 5 }) + .fontColor(Color.White) + .onClick((): void => { + if (this.backDisplaySyncSlow) { + this.backDisplaySyncSlow.stop(); + } + if (this.backDisplaySyncFast) { + this.backDisplaySyncFast.stop(); + } + router.back(); + }) + .width('20%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + } + .width('100%') + .justifyContent(FlexAlign.Center) + .shadow(ShadowStyle.OUTER_DEFAULT_SM) + .alignItems(VerticalAlign.Bottom) + .layoutWeight(1) + } + } +} +// [End display_sync_by_ui_complete_example] \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/PropertyAnimationDisplaySync.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/PropertyAnimationDisplaySync.ets new file mode 100644 index 0000000000000000000000000000000000000000..801acb33c9d0588b77af3e0cfe147e32e05d3e26 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/PropertyAnimationDisplaySync.ets @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 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. + */ +import { router } from '@kit.ArkUI'; + +// [Start display_sync_by_property_animation_complete_example] +@Entry +@Component +struct AnimationToAnimationDemo { + @State isAnimation: boolean = false; + @State translateX1: number = -100; + @State translateX2: number = -100; + @State translateX3: number = -100; + uiContext: UIContext | undefined = undefined; + + aboutToAppear() { + this.uiContext = this.getUIContext(); + if (!this.uiContext) { + console.warn('no uiContext'); + return; + } + } + + build() { + Column() { + Row() { + Text('30') + .fontWeight(FontWeight.Bold) + .fontSize(16) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .borderRadius(10) + .backgroundColor(0xF56C6C) + .width(80) + .height(80) + .translate({ x: this.translateX1 }) + } + .height('20%') + + Row() { + Text('40') + .fontWeight(FontWeight.Bold) + .fontSize(16) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .borderRadius(10) + .backgroundColor(0x2E8B57) + .width(80) + .height(80) + .translate({ x: this.translateX2 }) + } + .height('20%') + + Row() { + // [Start display_sync_property_animation] + Text('60') + // [StartExclude display_sync_property_animation] + .fontWeight(FontWeight.Bold) + .fontSize(16) + .fontColor(Color.White) + .textAlign(TextAlign.Center) + .borderRadius(10) + .backgroundColor(0x008B8B) + .width(80) + .height(80) + .translate({ x: this.translateX3 }) + // [EndExclude display_sync_property_animation] + .animation({ + duration: 1200, + iterations: 10, + playMode: PlayMode.AlternateReverse, + expectedFrameRateRange: { + expected: 60, + min: 0, + max: 120, + }, + }) + // [End display_sync_property_animation] + } + .height('20%') + + Row() { + // [Start display_sync_explicit_animation] + Button('Start') + // [StartExclude display_sync_explicit_animation] + .id('PropertyAnimationStart') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 10, left: 5 }) + .fontColor(Color.White) + .onClick(() => { + this.isAnimation = !this.isAnimation; + this.translateX3 = this.isAnimation ? 100 : -100; + // [EndExclude display_sync_explicit_animation] + + this.uiContext?.animateTo({ + duration: 1200, + iterations: 10, + playMode: PlayMode.AlternateReverse, + expectedFrameRateRange: { + expected: 30, + min: 0, + max: 120, + }, + }, () => { + this.translateX1 = this.isAnimation ? 100 : -100; + }) + // [End display_sync_explicit_animation] + + this.uiContext?.animateTo({ + duration: 1200, + iterations: 10, + playMode: PlayMode.AlternateReverse, + expectedFrameRateRange: { + expected: 40, + min: 0, + max: 120, + }, + }, () => { + this.translateX2 = this.isAnimation ? 100 : -100; + }) + }) + .width('40%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + + Button('Back') + .id('PropertyAnimationBack') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 10, left: 5 }) + .fontColor(Color.White) + .onClick((): void => { + router.back(); + }) + .width('40%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + } + .width('100%') + .justifyContent(FlexAlign.Center) + .shadow(ShadowStyle.OUTER_DEFAULT_SM) + .alignItems(VerticalAlign.Bottom) + .layoutWeight(1) + } + .width('100%') + .justifyContent(FlexAlign.Center) + .shadow(ShadowStyle.OUTER_DEFAULT_SM) + .layoutWeight(1) + } +} +// [End display_sync_by_property_animation_complete_example] \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/XComponentDisplaySync.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/XComponentDisplaySync.ets new file mode 100644 index 0000000000000000000000000000000000000000..aae4ce0275f57a9d63aad09660e7df4283fa750d --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/DispalySync/XComponentDisplaySync.ets @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2025 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. + */ +// [Start display_sync_create_xcomponent] +import XComponentContext from '../interface/XComponentContext'; +import { router } from '@kit.ArkUI'; + +@Entry +@Component +struct Index { + private xComponentContext1: XComponentContext | undefined = undefined; + private xComponentContext2: XComponentContext | undefined = undefined; + + build() { + Column() { + Row() { + // [StartExclude display_sync_create_xcomponent] + Text('30fps') + .fontWeight(FontWeight.Bold) + .fontSize(12) + .fontColor(Color.Red) + .textAlign(TextAlign.End) + .width(40) + .height(30) + // [EndExclude display_sync_create_xcomponent] + + XComponent({ + id: 'xcomponentId_30', + type: XComponentType.SURFACE, + libraryname: 'entry' + }) + .onLoad((xComponentContext) => { + this.xComponentContext1 = xComponentContext as XComponentContext; + }).width('640px') + .backgroundColor(Color.White) + + + }.height('40%') + + + Row() { + // [StartExclude display_sync_create_xcomponent] + Text('120fps') + .fontWeight(FontWeight.Bold) + .fontSize(12) + .fontColor(Color.Red) + .textAlign(TextAlign.End) + .width(40) + .height(30) + // [EndExclude display_sync_create_xcomponent] + + XComponent({ + id: 'xcomponentId_120', + type: XComponentType.SURFACE, + libraryname: 'entry' + }) + .onLoad((xComponentContext) => { + this.xComponentContext2 = xComponentContext as XComponentContext; + }).width('640px') + .backgroundColor(Color.White) + }.height('40%') + // [End display_sync_create_xcomponent] + + // [Start display_sync_start_and_stop_per_frame_callback] + Row() { + Button('Start') + .id('Start') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 20, right: 6, left: 6 }) + .onClick(() => { + if (this.xComponentContext1) { + this.xComponentContext1.register(); + } + if (this.xComponentContext2) { + this.xComponentContext2.register(); + } + }) + .width('30%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + + Button('Stop') + .id('Stop') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 20, left: 6 }) + .onClick(() => { + if (this.xComponentContext1) { + this.xComponentContext1.unregister(); + } + if (this.xComponentContext2) { + this.xComponentContext2.unregister(); + } + }) + .width('30%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + // [End display_sync_start_and_stop_per_frame_callback] + + Button('Back') + .id('Back') + .fontSize(14) + .fontWeight(500) + .margin({ bottom: 20, left: 6 }) + .onClick((): void => { + if (this.xComponentContext1) { + this.xComponentContext1.unregister(); + } + if (this.xComponentContext2) { + this.xComponentContext2.unregister(); + } + router.back(); + }) + .width('30%') + .height(40) + .shadow(ShadowStyle.OUTER_DEFAULT_LG) + } + .width('100%') + .height('20%') + } + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/entryability/EntryAbility.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..23a5f218909827e91bf6f66ce3d472c99df7c7c2 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 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. + */ +import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..69a47ce6bf1d1e2d0c5a0f0432a8bb83ef1daae4 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(0x0000, 'testTag', 'onBackup ok'); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(0x0000, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/interface/XComponentContext.ts b/ArkGraphics2D/DisplaySync/entry/src/main/ets/interface/XComponentContext.ts new file mode 100644 index 0000000000000000000000000000000000000000..821e80c2808c660d708a21470a209865f0926538 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/interface/XComponentContext.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 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. + */ +// [Start display_sync_export_interface_xcomponent_context] +export default interface XComponentContext { + register(): void; + + unregister(): void; +}; +// [End display_sync_export_interface_xcomponent_context] diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/pages/Index.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..fb9d6dd96ece3a33a890db159cdf74ab607c5391 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2025 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. + */ +import { router } from '@kit.ArkUI'; +import { hilog } from '@kit.PerformanceAnalysisKit'; + +@Entry +@Component +struct Index { + build() { + Column({ space: 20 }) { + Button("Requesting Frame Rates for Animations") + .id("AnimationFpsRequest") + .fontSize(16) + .fontWeight(500) + .margin({ top: 200, left: 6 }) + .onClick((): void => { + router.pushUrl({ + url: 'DispalySync/PropertyAnimationDisplaySync' + }, router.RouterMode.Standard, (err) => { + if (err) { + hilog.error(0x0001, "router.pushUrl", + "Invoke replaceUrl failed, code is %{public}s, message is %{public}s", + err.code, err.message); + return; + } + hilog.info(0x0002, "router.pushUrl", "Invoke replaceUrl succeeded."); + }); + }) + .width('80%') + .height(40) + + + Button('Requesting Frame Rates for UI Components') + .id('UiFpsRequest') + .fontSize(16) + .fontWeight(500) + .margin({ top: 20, left: 6 }) + .onClick((): void => { + router.pushUrl({ + url: 'DispalySync/CustomDrawDisplaySync' + }, router.RouterMode.Standard, (err) => { + if (err) { + hilog.error(0x0001, "router.pushUrl", + "Invoke replaceUrl failed, code is %{public}s, message is %{public}s.", + err.code, err.message); + return; + } + hilog.info(0x0002, "router.pushUrl", "Invoke replaceUrl succeeded."); + }); + }) + .width('80%') + .height(40) + + Button('Requesting Frame Rates for Custom Content') + .id('XComponent') + .fontSize(16) + .fontWeight(500) + .margin({ top: 20, left: 6 }) + .onClick((): void => { + router.pushUrl({ + url: 'DispalySync/XComponentDisplaySync' + }, router.RouterMode.Single, (err) => { + if (err) { + hilog.error(0x0001, "router.pushUrl", + "Invoke replaceUrl failed, code is %{public}s, message is %{public}s.", + err.code, err.message); + return; + } + hilog.info(0x0002, "router.pushUrl", "Invoke replaceUrl succeeded."); + }); + }) + .width('80%') + .height(40) + } + .width('100%') + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/ets/utils/Logger.ets b/ArkGraphics2D/DisplaySync/entry/src/main/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..3040a04a4383c918416e53a6344a7e2e48e85260 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/ets/utils/Logger.ets @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 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. + */ +import hilog from '@ohos.hilog'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + debug(...args: string[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Sample_DisplaySync]'); \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/module.json5 b/ArkGraphics2D/DisplaySync/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c75d702e2d350523978ecaf801c22762ce7d40c0 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/module.json5 @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/element/color.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/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/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/element/string.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..aa6d9f08b231e8d865a6328a2f28ec92a2acf753 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySync" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/background.png b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..f939c9fa8cc8914832e602198745f592a0dfa34d Binary files /dev/null and b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/background.png differ diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/foreground.png b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..4483ddad1f079e1089d685bd204ee1cfe1d01902 Binary files /dev/null and b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/foreground.png differ diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/layered_image.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..4f9ad6307a2bc56beb6d0fce0a49cbf213b20a74 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,6 @@ +{ + "layered-image": { + "background": "$media:background", + "foreground": "$media:foreground" + } +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/startIcon.png b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/media/startIcon.png differ diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/backup_config.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/main_pages.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..5c9db65f6d7ead000603ce2adeb339950d73d3a1 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,8 @@ +{ + "src": [ + "pages/Index", + "DispalySync/CustomDrawDisplaySync", + "DispalySync/PropertyAnimationDisplaySync", + "DispalySync/XComponentDisplaySync" + ] +} diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/en_US/element/string.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..aa6d9f08b231e8d865a6328a2f28ec92a2acf753 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySync" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/main/resources/zh_CN/element/string.json b/ArkGraphics2D/DisplaySync/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..3e449654fc7b0aec6c38a3c7b84531a29e79d203 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "Sample_DisplaySync" + } + ] +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/mock/mock-config.json5 b/ArkGraphics2D/DisplaySync/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..323d1d611fecf4ecb751976e3a71500b3712a445 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/mock/mock-config.json5 @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 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. + */ +{ +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/Ability.test.ets b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0ab410fb7c701d5fd9a426c96c793cf8ee8f7b04 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2025 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. + */ +import { describe, expect, it } from '@ohos/hypium'; +import { abilityDelegatorRegistry, Driver, ON } from '@kit.TestKit'; +import Logger from '../utils/Logger'; + +const TAG = '[Sample_DisplaySync]'; +const BUNDLE = 'MyApp_' +const DELAYMS_1S = 1000; +const DELAYMS_2S = 2000; +const DELAYMS_5S = 5000; + +const bundleName = abilityDelegatorRegistry.getArguments().bundleName; + +export default function abilityTest() { + describe('AbilityTest', () => { + it(BUNDLE + 'StartAbility_001, begin', 0, async (done: Function) => { + Logger.info(TAG, BUNDLE + 'StartAbility_001'); + let abilityDelegator: abilityDelegatorRegistry.AbilityDelegator = abilityDelegatorRegistry.getAbilityDelegator(); + try { + await abilityDelegator.startAbility({ + bundleName: bundleName, + abilityName: 'EntryAbility' + }); + } catch (err) { + Logger.info(TAG, `beforeAll exception = ${JSON.stringify(err)}`); + } + + Logger.info(TAG, BUNDLE + 'StartAbility_001, end'); + done(); + }); + + /** + * @tc.number:SUB_GRAPHIC_2D_HGM_DISPLAYSYNC_0200 + * @tc.name:PropertyAnimation_001 + * @tc.desc:PropertyAnimation + * @tc.size:MediumTest + * @tc.type:Function + * @tc.level:Level 1 + */ + it(BUNDLE + 'PropertyAnimation_001', 0, async (done: Function) => { + try { + Logger.info(TAG, BUNDLE + 'PropertyAnimation_001 beg' + + 'in'); + + let driver = Driver.create(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('AnimationFpsRequest')); + let propertyAnimationButton = await driver.findComponent(ON.id('AnimationFpsRequest')); + await propertyAnimationButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('PropertyAnimationStart')); + let startButton = await driver.findComponent(ON.id('PropertyAnimationStart')); + await startButton.click(); + await driver.delayMs(DELAYMS_5S); + + await driver.assertComponentExist(ON.id('PropertyAnimationBack')); + let backButton = await driver.findComponent(ON.id('PropertyAnimationBack')); + await backButton.click(); + await driver.delayMs(DELAYMS_1S); + + Logger.info(TAG, BUNDLE + 'PropertyAnimation_001 end'); + done(); + } catch (error) { + expect().assertFail(); + } + }); + + it(BUNDLE + 'UiFpsRequest_001', 0, async (done: Function) => { + try { + Logger.info(TAG, BUNDLE + 'UiFpsRequest_001, begin'); + + let driver = Driver.create(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('UiFpsRequest')); + let customDrawButton = await driver.findComponent(ON.id('UiFpsRequest')); + + await customDrawButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('CustomDrawStart')); + let startButton = await driver.findComponent(ON.id('CustomDrawStart')); + + await startButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('CustomDrawStop')); + let stopButton = await driver.findComponent(ON.id('CustomDrawStop')); + + await stopButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('CustomDrawBack')); + let backButton = await driver.findComponent(ON.id('CustomDrawBack')); + + await backButton.click(); + await driver.delayMs(DELAYMS_1S); + + Logger.info(TAG, BUNDLE + 'UiFpsRequest_001 end'); + done(); + } catch (error) { + expect().assertFail(); + } + }); + + /** + * @tc.number:SUB_GRAPHIC_2D_HGM_DISPLAYSYNC_0100 + * @tc.name:CustomDraw_001 + * @tc.desc:CustomDraw + * @tc.size:MediumTest + * @tc.type:Function + * @tc.level:Level 1 + */ + it(BUNDLE + 'CustomDraw_001', 0, async (done: Function) => { + try { + Logger.info(TAG, BUNDLE + 'CustomDraw_001 begin'); + + let driver = Driver.create(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('XComponent')); + + let customDrawButton = await driver.findComponent(ON.id('XComponent')); + + await customDrawButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('Start')); + + let startButton = await driver.findComponent(ON.id('Start')); + + await startButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('Stop')); + + let stopButton = await driver.findComponent(ON.id('Stop')); + + await stopButton.click(); + await driver.delayMs(DELAYMS_2S); + + await driver.assertComponentExist(ON.id('Back')); + + let backButton = await driver.findComponent(ON.id('Back')); + + await backButton.click(); + await driver.delayMs(DELAYMS_1S); + + Logger.info(TAG, BUNDLE + 'CustomDraw_001 end'); + done(); + } catch (error) { + expect().assertFail(); + } + }); + }) +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/List.test.ets b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..c64e0b06938d246ce044186d4b2d02b500a89e14 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/utils/Logger.ets b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/utils/Logger.ets new file mode 100644 index 0000000000000000000000000000000000000000..e6d55f5ac6aa03373a9cf02a7d4128ffa5175c8a --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/ets/utils/Logger.ets @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025 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. + */ + +import hilog from '@ohos.hilog'; + +class Logger { + private domain: number = 0xF811; + private prefix: string = ''; + private format: string = '%{public}s, %{public}s'; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xF811; + } + + debug(...args: string[]): void { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: string[]): void { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: string[]): void { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: string[]): void { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Sample_DisplaySync]'); \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/ohosTest/module.json5 b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..f6bdce9946cb02c0385e5a8836c133c93945013d --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/ohosTest/module.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/ArkGraphics2D/DisplaySync/entry/src/test/List.test.ets b/ArkGraphics2D/DisplaySync/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..a60c87c5cbb0badf7c3fd8975034590e6fafa992 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/test/List.test.ets @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 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. + */ +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/entry/src/test/LocalUnit.test.ets b/ArkGraphics2D/DisplaySync/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..b080060dcc059796d440db79c0ad94bed7b7bc79 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 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. + */ +import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } 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/ArkGraphics2D/DisplaySync/hvigor/hvigor-config.json5 b/ArkGraphics2D/DisplaySync/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..588ef752bc769d16dcf3826bfac354920c592813 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/hvigor/hvigor-config.json5 @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "modelVersion": "5.0.0", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | 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 */ + }, + "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/ArkGraphics2D/DisplaySync/hvigorfile.ts b/ArkGraphics2D/DisplaySync/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..d5e782664a81fec19737e2dff4dead3223a9b686 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/hvigorfile.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 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. + */ +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. */ +} diff --git a/ArkGraphics2D/DisplaySync/oh-package.json5 b/ArkGraphics2D/DisplaySync/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..fa0f78a6061227a18ae37179dd9fa76c6d5baa33 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/oh-package.json5 @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 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. + */ +{ + "modelVersion": "5.0.0", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.19", + "@ohos/hamock": "1.0.0" + } +} diff --git a/ArkGraphics2D/DisplaySync/ohosTest.md b/ArkGraphics2D/DisplaySync/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..a9138197dd9bf1a47499832bc535eebcff7d87c9 --- /dev/null +++ b/ArkGraphics2D/DisplaySync/ohosTest.md @@ -0,0 +1,19 @@ +# DisplaySync测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|-----------------|-----------------|----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------|------|------| +| 拉起应用 | 设备正常运行 | | 成功拉起应用 | 是 | Pass | +| 主页展示 | 设备正常运行 | | 展示 Requesting Frame Rates for Animations、Requesting Frame Rates for UI Components、Requesting Frame Rates for Custom Content 按钮 | 是 | Pass | +| 主页按钮点击 | 位于主页 | 点击 Requesting Frame Rates for Animations | 跳转至请求动画绘制帧率页面 | 是 | Pass | +| 动画绘制帧率开始 | 位于动画绘制帧率面 | 点击Start | 三个方块分别按照30hz、40hz、60hz频率来回移动 | 是 | Pass | +| 动画绘制帧率返回主页 | 位于动画绘制帧率页面 | 点击Back | 回到主页 | 是 | Pass | +| 主页按钮点击 | 位于主页 | 点击 Requesting Frame Rates for UI Components | 跳转请求 UI 绘制帧率页面 | 是 | Pass | +| 请求 UI 绘制帧率开始 | 位于请求 UI 绘制帧率 | 点击 Start | 数字 30、60 分别按照 30hz、60hz 频率放大缩小 | 是 | Pass | +| 请求 UI 绘制帧率停止 | 位于请求 UI 绘制帧率 | 点击 Stop | 数字 30、60 变化停止 | 是 | Pass | +| 请求 UI 绘制帧率返回 | 位于请求 UI 绘制帧率 | 点击 Back | 回到主页 | 是 | Pass | +| 主页按钮点击 | 位于主页 | 点击 Requesting Frame Rates for Custom Content | 跳转 请求自绘制内容绘制帧率 页面 | 是 | Pass | +| 请求自绘制内容绘制帧率开始 | 位于请求自绘制内容绘制帧率页面 | 点击Start | 两个方块分别按照 30hz、120hz 频率来回移动 | 是 | pass | +| 请求自绘制内容绘制帧率停止 | 位于请求自绘制内容绘制帧率页面 | 点击Stop | 两个方块移动停止 | 是 | pass | +| 请求自绘制内容绘制帧率返回主页 | 位于请求自绘制内容绘制帧率页面 | 点击Back | 回到主页 | 是 | pass | \ No newline at end of file diff --git a/ArkGraphics2D/DisplaySync/screenshots/device/UI.png b/ArkGraphics2D/DisplaySync/screenshots/device/UI.png new file mode 100644 index 0000000000000000000000000000000000000000..edaa1bd7b952da56ada1f17f230bedc87f3b4d6b Binary files /dev/null and b/ArkGraphics2D/DisplaySync/screenshots/device/UI.png differ diff --git a/ArkGraphics2D/DisplaySync/screenshots/device/XComponent.png b/ArkGraphics2D/DisplaySync/screenshots/device/XComponent.png new file mode 100644 index 0000000000000000000000000000000000000000..77d32d82edab02be8a93f43561283454764744f9 Binary files /dev/null and b/ArkGraphics2D/DisplaySync/screenshots/device/XComponent.png differ diff --git a/ArkGraphics2D/DisplaySync/screenshots/device/animation.png b/ArkGraphics2D/DisplaySync/screenshots/device/animation.png new file mode 100644 index 0000000000000000000000000000000000000000..ec438b209234337590d796a9fa445f08f6d5f9a2 Binary files /dev/null and b/ArkGraphics2D/DisplaySync/screenshots/device/animation.png differ diff --git a/ArkGraphics2D/DisplaySync/screenshots/device/index.png b/ArkGraphics2D/DisplaySync/screenshots/device/index.png new file mode 100644 index 0000000000000000000000000000000000000000..d8949b46e924459b59871c0351703ba0b8fedeee Binary files /dev/null and b/ArkGraphics2D/DisplaySync/screenshots/device/index.png differ