* Copyright (c) 2026 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 "core/common/container_consts.h"
#include "core/pipeline/base/element_register.h"
#include "frameworks/base/utils/string_utils.h"
#include "interfaces/inner_api/ace_kit/include/ui/base/ace_type.h"
#include "interfaces/napi/kits/utils/napi_utils.h"
#include "interfaces/napi/kits/luminance_sampler/js_luminance_sampler.h"
namespace OHOS::Ace::Napi {
static const TargetInfo INVALID_TARGET_INFO = TargetInfo();
static constexpr uint32_t PARAM_SIZE_TWO = 2;
LuminanceSampler::LuminanceSampler() {}
LuminanceSampler& LuminanceSampler::GetInstance()
{
static LuminanceSampler instance;
return instance;
}
bool LuminanceSampler::GetFrameNodeTargetID(TargetInfo& info, int32_t& result)
{
result = -1;
if (info == INVALID_TARGET_INFO) {
return false;
}
if (info.idIsNumber) {
result = StringUtils::StringToInt(info.id);
} else {
std::string targetIdString = info.id;
auto componentId = info.componentId;
if (componentId != -1) {
auto targetComponentIdNode =
ElementRegister::GetInstance()->GetSpecificItemById<NG::UINode>(componentId);
CHECK_NULL_RETURN(targetComponentIdNode, false);
auto targetNode = NG::FrameNode::FindChildByNameUINode(targetComponentIdNode, targetIdString);
CHECK_NULL_RETURN(targetNode, false);
result = targetNode->GetId();
} else {
auto targetNode =
ElementRegister::GetInstance()->GetAttachedFrameNodeById(targetIdString, false, Container::CurrentId());
if (!targetNode) {
targetNode = ElementRegister::GetInstance()->GetAttachedFrameNodeById(targetIdString);
}
CHECK_NULL_RETURN(targetNode, false);
result = targetNode->GetId();
}
}
if (result < 0) {
return false;
}
return true;
}
TargetInfo LuminanceSampler::ParseTargetInfo(napi_env& env, napi_callback_info& info)
{
size_t argc = 2;
napi_value argv[2] = { nullptr };
napi_value thisVar = nullptr;
napi_status status = napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr);
TargetInfo result;
napi_value targetInfoId = nullptr;
napi_value targetInfoComponentId = nullptr;
napi_valuetype valueType = napi_undefined;
if (argc < PARAM_SIZE_TWO) {
return result;
}
status = napi_typeof(env, argv[0], &valueType);
status = napi_typeof(env, argv[1], &valueType);
if (status != napi_ok) {
NapiThrow(env, "Incorrect parameters types: Failed to get argument type.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
if (valueType == napi_object) {
status = napi_get_named_property(env, argv[1], "id", &targetInfoId);
if (status != napi_ok) {
NapiThrow(env, "Incorrect parameters types: Failed to get 'id' property.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
status = napi_get_named_property(env, argv[1], "componentId", &targetInfoComponentId);
if (status != napi_ok) {
NapiThrow(
env, "Incorrect parameters types: Failed to get 'componentId' property.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
napi_valuetype idType;
status = napi_typeof(env, targetInfoId, &idType);
if (status != napi_ok) {
NapiThrow(env, "Incorrect parameters types: Failed to get 'id' type.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
if (idType == napi_string) {
size_t idLength = 0;
status = napi_get_value_string_utf8(env, targetInfoId, nullptr, 0, &idLength);
std::unique_ptr<char[]> idValue = std::make_unique<char[]>(idLength + 1);
napi_get_value_string_utf8(env, targetInfoId, idValue.get(), idLength + 1, &idLength);
if (status != napi_ok) {
NapiThrow(
env, "Incorrect parameters types: Failed to get 'id' value as string.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
result.id = std::string(idValue.get(), idLength);
result.idIsNumber = false;
} else if (idType == napi_number) {
int32_t idValue;
status = napi_get_value_int32(env, targetInfoId, &idValue);
if (status != napi_ok) {
NapiThrow(
env, "Incorrect parameters types: Failed to get 'id' value as number.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
result.id = std::to_string(idValue);
result.idIsNumber = true;
} else {
NapiThrow(env, "Incorrect parameters types: The type of 'id' is incorrect.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
if (targetInfoComponentId != nullptr) {
napi_valuetype componentIdType;
status = napi_typeof(env, targetInfoComponentId, &componentIdType);
if (status != napi_ok) {
NapiThrow(
env, "Incorrect parameters types: Failed to get 'componentId' type.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
if (componentIdType == napi_number) {
int32_t componentIdValue;
status = napi_get_value_int32(env, targetInfoComponentId, &componentIdValue);
if (status != napi_ok) {
NapiThrow(env, "Incorrect parameters types: Failed to get 'componentId' value as number.",
ERROR_CODE_INTERNAL_ERROR);
return result;
}
result.componentId = componentIdValue;
} else if (componentIdType != napi_undefined && componentIdType != napi_null) {
NapiThrow(env, "Incorrect parameters types: The type of 'componentId' is incorrect.",
ERROR_CODE_INTERNAL_ERROR);
return result;
}
}
} else if (valueType != napi_undefined && valueType != napi_null) {
NapiThrow(env, "Incorrect parameters types: The type of parameters is incorrect.", ERROR_CODE_INTERNAL_ERROR);
return result;
}
return result;
}
SamplerInstance* LuminanceSampler::GetSamplerInstance(napi_env env, napi_callback_info info)
{
SamplerInstance* samplerInstance = nullptr;
napi_value thisVar;
napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
napi_unwrap(env, thisVar, reinterpret_cast<void**>(&samplerInstance));
return samplerInstance;
}
napi_value LuminanceSampler::JSSetBackgroundLuminanceSamplingOptions(napi_env env, napi_callback_info info)
{
auto samplerInstance = GetSamplerInstance(env, info);
if (!samplerInstance) {
return nullptr;
}
samplerInstance->SetBackgroundLuminanceSamplingOptions(env, info);
return nullptr;
}
napi_value LuminanceSampler::JSOnBackgroundLuminanceChange(napi_env env, napi_callback_info info)
{
auto samplerInstance = GetSamplerInstance(env, info);
if (!samplerInstance) {
return nullptr;
}
samplerInstance->OnBackgroundLuminanceChange(env, info);
return nullptr;
}
napi_value LuminanceSampler::JSOffBackgroundLuminanceChange(napi_env env, napi_callback_info info)
{
auto samplerInstance = GetSamplerInstance(env, info);
if (!samplerInstance) {
return nullptr;
}
samplerInstance->OffBackgroundLuminanceChange(env, info);
return nullptr;
}
napi_value LuminanceSampler::CreateJSSamplerInstance(napi_env env, SamplerInstance* samplerInstance)
{
napi_value jsSampler = nullptr;
napi_create_object(env, &jsSampler);
napi_wrap(
env, jsSampler, samplerInstance,
[](napi_env env, void* data, void* hint) {
SamplerInstance* samplerInstance = (SamplerInstance*)data;
samplerInstance->Destroy(env);
},
nullptr, nullptr);
napi_property_descriptor resultFuncs[] = {
DECLARE_NAPI_FUNCTION("setBackgroundLuminanceSamplingConfigs", JSSetBackgroundLuminanceSamplingOptions),
DECLARE_NAPI_FUNCTION("onBackgroundLuminanceChange", JSOnBackgroundLuminanceChange),
DECLARE_NAPI_FUNCTION("offBackgroundLuminanceChange", JSOffBackgroundLuminanceChange)
};
NAPI_CALL(env, napi_define_properties(env, jsSampler, sizeof(resultFuncs) / sizeof(resultFuncs[0]), resultFuncs));
return jsSampler;
}
RefPtr<NG::FrameNode> LuminanceSampler::GetTargetFrameNode(int32_t targetNodeID)
{
auto targetNodeElement = ElementRegister::GetInstance()->GetNodeById(targetNodeID);
CHECK_NULL_RETURN(targetNodeElement, nullptr);
auto targetNode = AceType::DynamicCast<NG::FrameNode>(targetNodeElement);
return targetNode;
}
napi_value LuminanceSampler::JSGetOrCreateLuminanceSampler(napi_env& env, napi_callback_info& info)
{
auto targetInfo = ParseTargetInfo(env, info);
int32_t targetInfoId = -1;
if (!GetFrameNodeTargetID(targetInfo, targetInfoId)) {
return nullptr;
}
auto targetNodePtr = GetTargetFrameNode(targetInfoId);
CHECK_NULL_RETURN(targetNodePtr, nullptr);
auto registedInstance = targetNodePtr->GetSamplerManager();
TAG_LOGD(AceLogTag::ACE_VISUAL_EFFECT,
"LuminanceSampling | {id:%{public}d,tag:%{public}s,inspectorId:%{public}s} | registedInstance: %{public}d",
targetNodePtr->GetId(), targetNodePtr->GetTag().c_str(), targetNodePtr->GetInspectorId().value_or("").c_str(),
registedInstance ? 1 : 0);
if (!registedInstance) {
SamplerInstance* samplerInstance = new SamplerInstance();
samplerInstance->IncRefCount();
if (!samplerInstance->SetTargetNode(targetInfoId)) {
samplerInstance->DecRefCount();
return nullptr;
}
auto newSamplerInstance = CreateJSSamplerInstance(env, samplerInstance);
RefPtr<NG::SamplerManager> newSamplerInstanceRef =
AceType::MakeRefPtr<SamplerManagerImpl>(env, newSamplerInstance);
targetNodePtr->SetSamplerManager(newSamplerInstanceRef);
return newSamplerInstance;
}
auto registedInstanceImpl = AceType::DynamicCast<SamplerManagerImpl>(registedInstance);
CHECK_NULL_RETURN(registedInstanceImpl, nullptr);
auto jsInstance = registedInstanceImpl->GetJSInstance();
return jsInstance;
}
napi_value GetOrCreateLuminanceSampler(napi_env env, napi_callback_info info)
{
return LuminanceSampler::GetInstance().JSGetOrCreateLuminanceSampler(env, info);
}
static napi_value LuminanceSamplerExport(napi_env env, napi_value exports)
{
napi_property_descriptor properties[] = {
DECLARE_NAPI_FUNCTION("getOrCreateLuminanceSampler", GetOrCreateLuminanceSampler),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(properties) / sizeof(properties[0]), properties));
return exports;
}
static napi_module luminance_sampler_module = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = LuminanceSamplerExport,
.nm_modname = "luminancesampler",
.nm_priv = ((void*)0),
.reserved = { 0 },
};
extern "C" __attribute__((constructor)) void RegisterLuminanceSampler()
{
napi_module_register(&luminance_sampler_module);
}
}