/* * Copyright (c) 2021-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 "bridge/declarative_frontend/jsview/js_image.h" #include #include #include #ifndef PREVIEW #include #endif #include "interfaces/inner_api/ace/ai/image_analyzer.h" #include "interfaces/inner_api/ui_session/ui_session_manager.h" #include "base/geometry/ng/vector.h" #include "base/image/drawable_descriptor.h" #include "base/image/drawing_color_filter.h" #include "base/image/drawing_lattice.h" #include "base/image/pixel_map.h" #include "base/log/ace_scoring_log.h" #include "base/log/ace_trace.h" #include "bridge/common/utils/engine_helper.h" #include "bridge/declarative_frontend/engine/functions/js_drag_function.h" #include "bridge/declarative_frontend/engine/functions/js_event_function.h" #include "bridge/declarative_frontend/engine/js_ref_ptr.h" #include "bridge/declarative_frontend/engine/js_types.h" #include "bridge/declarative_frontend/engine/jsi/js_ui_index.h" #include "bridge/declarative_frontend/jsview/models/image_model_impl.h" #include "core/common/container.h" #include "core/common/resource/resource_parse_utils.h" #include "core/components/common/layout/constants.h" #include "core/components/image/image_event.h" #include "core/components/image/image_theme.h" #include "core/components_ng/base/view_abstract_model.h" #include "core/components_ng/base/view_stack_processor.h" #include "core/components_ng/event/gesture_event_hub.h" #include "core/components_ng/pattern/image/image_model.h" #include "core/components_ng/pattern/image/image_model_ng.h" #include "core/image/image_source_info.h" namespace { const std::vector DEFAULT_COLORFILTER_MATRIX = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 }; constexpr float CEIL_SMOOTHEDGE_VALUE = 1.333f; constexpr float FLOOR_SMOOTHEDGE_VALUE = 0.334f; constexpr float DEFAULT_SMOOTHEDGE_VALUE = 0.0f; constexpr float DEFAULT_HDR_BRIGHTNESS = 1.0f; constexpr float HDR_BRIGHTNESS_MIN = 0.0f; constexpr float HDR_BRIGHTNESS_MAX = 1.0f; constexpr uint32_t FIT_MATRIX = 16; constexpr char DRAWABLE_DESCRIPTOR_NAME[] = "DrawableDescriptor"; constexpr char LAYERED_DRAWABLE_DESCRIPTOR_NAME[] = "LayeredDrawableDescriptor"; constexpr char ANIMATED_DRAWABLE_DESCRIPTOR_NAME[] = "AnimatedDrawableDescriptor"; constexpr char PIXELMAP_DRAWABLE_DESCRIPTOR_NAME[] = "PixelMapDrawableDescriptor"; } // namespace namespace OHOS::Ace { namespace { ImageSourceInfo CreateSourceInfo(const std::shared_ptr& srcRef, RefPtr& pixmap, const std::string& bundleName, const std::string& moduleName) { #if defined(PIXEL_MAP_SUPPORTED) if (pixmap) { return ImageSourceInfo(pixmap); } #endif return { srcRef, bundleName, moduleName }; } } // namespace std::unique_ptr ImageModel::instance_ = nullptr; std::mutex ImageModel::mutex_; ImageModel* __attribute__((optnone)) ImageModel::GetInstance() { if (!instance_) { std::lock_guard lock(mutex_); if (!instance_) { #ifdef NG_BUILD instance_.reset(new NG::ImageModelNG()); #else if (Container::IsCurrentUseNewPipeline()) { instance_.reset(new NG::ImageModelNG()); } else { instance_.reset(new Framework::ImageModelImpl()); } #endif } } return instance_.get(); } } // namespace OHOS::Ace namespace OHOS::Ace::Framework { JSRef LoadImageSuccEventToJSValue(const LoadImageSuccessEvent& eventInfo) { JSRef obj = JSRef::New(); obj->SetProperty("width", eventInfo.GetWidth()); obj->SetProperty("height", eventInfo.GetHeight()); obj->SetProperty("componentWidth", eventInfo.GetComponentWidth()); obj->SetProperty("componentHeight", eventInfo.GetComponentHeight()); obj->SetProperty("loadingStatus", eventInfo.GetLoadingStatus()); obj->SetProperty("contentWidth", eventInfo.GetContentWidth()); obj->SetProperty("contentHeight", eventInfo.GetContentHeight()); obj->SetProperty("contentOffsetX", eventInfo.GetContentOffsetX()); obj->SetProperty("contentOffsetY", eventInfo.GetContentOffsetY()); return JSRef::Cast(obj); } JSRef LoadImageFailEventToJSValue(const LoadImageFailEvent& eventInfo) { JSRef obj = JSRef::New(); obj->SetProperty("componentWidth", eventInfo.GetComponentWidth()); obj->SetProperty("componentHeight", eventInfo.GetComponentHeight()); obj->SetProperty("message", eventInfo.GetErrorMessage()); auto businessErrorObj = JSRef::New(); businessErrorObj->SetProperty("code", static_cast(eventInfo.GetErrorInfo().errorCode)); businessErrorObj->SetProperty("message", eventInfo.GetErrorInfo().errorMessage); obj->SetPropertyObject("error", businessErrorObj); return JSRef::Cast(obj); } void JSImage::SetAlt(const JSCallbackInfo& args) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } if (args.Length() < 1) { return; } auto context = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(context); bool isCard = context->IsFormRender(); std::string src; bool srcValid = false; RefPtr resObj; if (args[0]->IsString()) { src = args[0]->ToString(); } else { srcValid = ParseJsMedia(args[0], src, resObj); } if (ImageSourceInfo::ResolveURIType(src) == SrcType::NETWORK) { return; } int32_t resId = 0; if (args[0]->IsObject()) { JSRef jsObj = JSRef::Cast(args[0]); JSRef tmp = jsObj->GetProperty("id"); if (!tmp->IsNull() && tmp->IsNumber()) { resId = tmp->ToNumber(); } } std::string bundleName; std::string moduleName; GetJsMediaBundleInfo(args[0], bundleName, moduleName); RefPtr pixmap = nullptr; // input is Drawable if (!srcValid && !isCard) { #if defined(PIXEL_MAP_SUPPORTED) pixmap = CreatePixelMapFromNapiValue(args[0]); #endif } auto srcRef = std::make_shared(src); auto srcInfo = CreateSourceInfo(srcRef, pixmap, bundleName, moduleName); srcInfo.SetIsUriPureNumber((resId == -1)); ImageModel::GetInstance()->SetAlt(srcInfo); if (SystemProperties::ConfigChangePerform()) { ImageModel::GetInstance()->CreateWithResourceObj(ImageResourceType::ALT, resObj); } } void JSImage::SetObjectFit(const JSCallbackInfo& args) { if (args.Length() < 1) { ImageModel::GetInstance()->SetImageFit(ImageFit::COVER); return; } int32_t parseRes = 2; ParseJsInteger(args[0], parseRes); if (parseRes < static_cast(ImageFit::FILL) || parseRes > static_cast(ImageFit::MATRIX)) { parseRes = 2; } auto fit = static_cast(parseRes); if (parseRes == FIT_MATRIX) { fit = ImageFit::MATRIX; } ImageModel::GetInstance()->SetImageFit(fit); } void JSImage::SetImageMatrix(const JSCallbackInfo& args) { if (args.Length() > 0) { auto jsVal = args[0]; if (!jsVal->IsObject()) { SetDefaultImageMatrix(); return; } JSRef array = JSRef::Cast(jsVal)->GetProperty(static_cast(ArkUIIndex::MATRIX4X4)); const auto matrix4Len = Matrix4::DIMENSION * Matrix4::DIMENSION; if (!array->IsArray()) { return; } JSRef jsArray = JSRef::Cast(array); if (jsArray->Length() != matrix4Len) { return; } std::vector matrix(matrix4Len); for (int32_t i = 0; i < matrix4Len; i++) { double value = 0.0; ParseJsDouble(jsArray->GetValueAt(i), value); matrix[i] = static_cast(value); } Matrix4 setValue = Matrix4(matrix[0], matrix[4], matrix[8], matrix[12], matrix[1], matrix[5], matrix[9], matrix[13], matrix[2], matrix[6], matrix[10], matrix[14], matrix[3], matrix[7], matrix[11], matrix[15]); ImageModel::GetInstance()->SetImageMatrix(setValue); } else { SetDefaultImageMatrix(); return; } } void JSImage::SetDefaultImageMatrix() { const auto matrix4Len = Matrix4::DIMENSION * Matrix4::DIMENSION; std::vector matrix(matrix4Len); const int32_t initPosition = 5; for (int32_t i = 0; i < matrix4Len; i = i + initPosition) { matrix[i] = 1.0f; } Matrix4 setValue = Matrix4(matrix[0], matrix[4], matrix[8], matrix[12], matrix[1], matrix[5], matrix[9], matrix[13], matrix[2], matrix[6], matrix[10], matrix[14], matrix[3], matrix[7], matrix[11], matrix[15]); ImageModel::GetInstance()->SetImageMatrix(setValue); } void JSImage::SetMatchTextDirection(bool value) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } ImageModel::GetInstance()->SetMatchTextDirection(value); } void JSImage::SetFitOriginalSize(bool value) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } ImageModel::GetInstance()->SetFitOriginSize(value); } void JSImage::SetBorder(const Border& border) { ImageModel::GetInstance()->SetBorder(border); } void JSImage::OnComplete(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto jsLoadSuccFunc = AceType::MakeRefPtr>( JSRef::Cast(args[0]), LoadImageSuccEventToJSValue); auto onComplete = [execCtx = args.GetExecutionContext(), func = std::move(jsLoadSuccFunc)]( const LoadImageSuccessEvent& info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Image.onComplete"); func->Execute(info); UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Image.onComplete"); }; ImageModel::GetInstance()->SetOnComplete(std::move(onComplete)); } } void JSImage::OnError(const JSCallbackInfo& args) { if (args[0]->IsFunction()) { auto jsLoadFailFunc = AceType::MakeRefPtr>( JSRef::Cast(args[0]), LoadImageFailEventToJSValue); auto onError = [execCtx = args.GetExecutionContext(), func = std::move(jsLoadFailFunc)]( const LoadImageFailEvent& info) { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Image.onError"); func->Execute(info); UiSessionManager::GetInstance()->ReportComponentChangeEvent("event", "Image.onError"); }; ImageModel::GetInstance()->SetOnError(onError); } } void JSImage::OnFinish(const JSCallbackInfo& info) { auto tmpInfo = info[0]; if (!tmpInfo->IsFunction()) { return; } RefPtr jsFunc = AceType::MakeRefPtr(JSRef(), JSRef::Cast(tmpInfo)); WeakPtr targetNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onFinish = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc), node = targetNode]() { JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx); ACE_SCORING_EVENT("Image.onFinish"); PipelineContext::SetCallBackNode(node); func->Execute(); }; ImageModel::GetInstance()->SetSvgAnimatorFinishEvent(onFinish); } void JSImage::Create(const JSCallbackInfo& info) { if (info.Length() < 1) { return; } CreateImage(info); } void JSImage::CheckIsCard(std::string& src, const JSRef& imageInfo) { bool isCard = false; auto container = Container::Current(); if (container) { isCard = container->IsFormRender() && !container->IsDynamicRender(); } else { TAG_LOGW(AceLogTag::ACE_IMAGE, "check is card, container is null"); } if (isCard && imageInfo->IsString()) { SrcType srcType = ImageSourceInfo::ResolveURIType(src); bool notSupport = (srcType == SrcType::NETWORK || srcType == SrcType::FILE || srcType == SrcType::DATA_ABILITY); if (notSupport) { src.clear(); } } } // The image reset only when the param is ImageContent.Empty bool JSImage::CheckResetImage(const bool& srcValid, const JSCallbackInfo& info) { if (!srcValid) { int32_t parseRes = -1; if (info.Length() < 1 || !ParseJsInteger(info[0], parseRes)) { return false; } ImageModel::GetInstance()->ResetImage(); return true; } return false; } ImageType JSImage::ParseImageType(const JSRef& imageInfo) { if (!imageInfo->IsObject()) { return ImageType::BASE; } JSRef jsObj = JSRef::Cast(imageInfo); if (jsObj->IsUndefined()) { return ImageType::BASE; } JSRef jsTypeName = jsObj->GetProperty("typeName"); if (!jsTypeName->IsString()) { return ImageType::BASE; } auto typeName = jsTypeName->ToString(); if (typeName == DRAWABLE_DESCRIPTOR_NAME) { return ImageType::DRAWABLE; } else if (typeName == LAYERED_DRAWABLE_DESCRIPTOR_NAME) { return ImageType::LAYERED_DRAWABLE; } else if (typeName == ANIMATED_DRAWABLE_DESCRIPTOR_NAME) { return ImageType::ANIMATED_DRAWABLE; } else if (typeName == PIXELMAP_DRAWABLE_DESCRIPTOR_NAME) { return ImageType::PIXELMAP_DRAWABLE; } else { return ImageType::BASE; } } void JSImage::CreateImage(const JSCallbackInfo& info, bool isImageSpan) { std::string bundleName; std::string moduleName; std::string src; auto imageInfo = info[0]; int32_t resId = 0; RefPtr resObj; bool srcValid = ParseJsMediaWithBundleName(imageInfo, src, bundleName, moduleName, resId, resObj); CHECK_EQUAL_VOID(CheckResetImage(srcValid, info), true); CheckIsCard(src, imageInfo); RefPtr pixmap = nullptr; if (!srcValid) { #ifdef PIXEL_MAP_SUPPORTED auto type = ParseImageType(imageInfo); if (type == ImageType::ANIMATED_DRAWABLE) { std::vector> pixelMaps; int32_t duration = -1; int32_t iterations = 1; if (GetPixelMapListFromAnimatedDrawable(imageInfo, pixelMaps, duration, iterations)) { CreateImageAnimation(pixelMaps, duration, iterations); return; } } else if (type == ImageType::PIXELMAP_DRAWABLE) { auto* address = UnwrapNapiValue(imageInfo); auto drawable = DrawableDescriptor::CreateDrawable(address); if (!drawable) { return; } if (drawable->GetDrawableSrcType() == 1) { pixmap = GetDrawablePixmap(imageInfo); } else { ImageModel::GetInstance()->Create(drawable); ParseImageAIOptions(info); return; } } else if (type == ImageType::LAYERED_DRAWABLE || type == ImageType::DRAWABLE) { pixmap = GetDrawablePixmap(imageInfo); } else { pixmap = CreatePixelMapFromNapiValue(imageInfo); } #endif } ImageInfoConfig config; config.src = std::make_shared(src); config.bundleName = bundleName; config.moduleName = moduleName; config.isUriPureNumber = (resId == -1); config.isImageSpan = isImageSpan; ImageModel::GetInstance()->Create(config, pixmap); ParseImageAIOptions(info); if (SystemProperties::ConfigChangePerform() && resObj) { ImageModel::GetInstance()->CreateWithResourceObj(ImageResourceType::SRC, resObj); } } void JSImage::ParseImageAIOptions(const JSCallbackInfo& info) { if (info.Length() <= 1) { return; } auto options = info[1]; if (!options->IsObject()) { return; } auto engine = EngineHelper::GetCurrentEngine(); CHECK_NULL_VOID(engine); NativeEngine* nativeEngine = engine->GetNativeEngine(); panda::Local value = options.Get().GetLocalHandle(); JSValueWrapper valueWrapper = value; ScopeRAII scope(reinterpret_cast(nativeEngine)); napi_value optionsValue = nativeEngine->ValueToNapiValue(valueWrapper); ImageModel::GetInstance()->SetImageAIOptions(optionsValue); } bool JSImage::IsDrawable(const JSRef& jsValue) { if (!jsValue->IsObject()) { return false; } JSRef jsObj = JSRef::Cast(jsValue); if (jsObj->IsUndefined()) { return false; } // if jsObject has function getPixelMap, it's a DrawableDescriptor object JSRef func = jsObj->GetProperty("getPixelMap"); return (!func->IsNull() && func->IsFunction()); } void JSImage::JsBorder(const JSCallbackInfo& info) { if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) { JSViewAbstract::JsBorder(info); ImageModel::GetInstance()->SetBackBorder(); return; } // handles generic property logic. JSViewAbstract::JsBorder(info); // handles the image component separately, aiming to extract and set the borderRadius property if (!info[0]->IsObject()) { CalcDimension borderRadius; ImageModel::GetInstance()->SetBorderRadius(borderRadius); return; } JSRef object = JSRef::Cast(info[0]); auto valueRadius = object->GetProperty(static_cast(ArkUIIndex::RADIUS)); if (!valueRadius->IsUndefined()) { ParseBorderRadius(valueRadius); } } void ParseImageAllBorderRadiusesResObj(NG::BorderRadiusProperty& borderRadius, const RefPtr& topLeftResObj, const RefPtr& topRightResObj, const RefPtr& bottomLeftResObj, const RefPtr& bottomRightResObj) { if (!SystemProperties::ConfigChangePerform()) { return; } if (topLeftResObj) { auto&& updateFunc = [](const RefPtr& resObj, NG::BorderRadiusProperty& borderRadius) { CalcDimension result; ResourceParseUtils::ParseResDimensionVp(resObj, result); borderRadius.radiusTopLeft = result; }; borderRadius.AddResource("borderRadius.topLeft", topLeftResObj, std::move(updateFunc)); } if (topRightResObj) { auto&& updateFunc = [](const RefPtr& resObj, NG::BorderRadiusProperty& borderRadius) { CalcDimension result; ResourceParseUtils::ParseResDimensionVp(resObj, result); borderRadius.radiusTopRight = result; }; borderRadius.AddResource("borderRadius.topRight", topRightResObj, std::move(updateFunc)); } if (bottomLeftResObj) { auto&& updateFunc = [](const RefPtr& resObj, NG::BorderRadiusProperty& borderRadius) { CalcDimension result; ResourceParseUtils::ParseResDimensionVp(resObj, result); borderRadius.radiusBottomLeft = result; }; borderRadius.AddResource("borderRadius.bottomLeft", bottomLeftResObj, std::move(updateFunc)); } if (bottomRightResObj) { auto&& updateFunc = [](const RefPtr& resObj, NG::BorderRadiusProperty& borderRadius) { CalcDimension result; ResourceParseUtils::ParseResDimensionVp(resObj, result); borderRadius.radiusBottomRight = result; }; borderRadius.AddResource("borderRadius.bottomRight", bottomRightResObj, std::move(updateFunc)); } } void SetImageBorderRadius(const CalcDimension& topLeft, const CalcDimension& topRight, const CalcDimension& bottomLeft, const CalcDimension& bottomRight) { ImageModel::GetInstance()->SetBorderRadius(topLeft, topRight, bottomLeft, bottomRight); ViewAbstractModel::GetInstance()->SetBorderRadius(topLeft, topRight, bottomLeft, bottomRight); } void JSImage::ParseBorderRadius(const JSRef& args) { CalcDimension borderRadius; RefPtr borderRadiusResObj; if (ParseJsDimensionVp(args, borderRadius, borderRadiusResObj)) { if (SystemProperties::ConfigChangePerform() && borderRadiusResObj) { ViewAbstractModel::GetInstance()->SetBorderRadius(borderRadiusResObj); } else { ViewAbstractModel::GetInstance()->SetBorderRadius(borderRadius); } if (SystemProperties::ConfigChangePerform()) { ImageModel::GetInstance()->CreateWithResourceObj(ImageResourceType::BORDER_RADIUS, borderRadiusResObj); } ImageModel::GetInstance()->SetBorderRadius(borderRadius); } else if (args->IsObject()) { JSRef object = JSRef::Cast(args); CalcDimension topLeft; CalcDimension topRight; CalcDimension bottomLeft; CalcDimension bottomRight; if (ParseAllBorderRadiuses(object, topLeft, topRight, bottomLeft, bottomRight)) { ImageModel::GetInstance()->SetBorderRadius( GetLocalizedBorderRadius(topLeft, topRight, bottomLeft, bottomRight)); ViewAbstractModel::GetInstance()->SetBorderRadius( GetLocalizedBorderRadius(topLeft, topRight, bottomLeft, bottomRight)); return; } if (SystemProperties::ConfigChangePerform()) { NG::BorderRadiusProperty borderRadiusProperty; RefPtr topLeftResObj; RefPtr topRightResObj; RefPtr bottomLeftResObj; RefPtr bottomRightResObj; GetBorderRadiusResObj("topLeft", object, topLeft, topLeftResObj); GetBorderRadiusResObj("topRight", object, topRight, topRightResObj); GetBorderRadiusResObj("bottomLeft", object, bottomLeft, bottomLeftResObj); GetBorderRadiusResObj("bottomRight", object, bottomRight, bottomRightResObj); borderRadiusProperty.radiusTopLeft = topLeft; borderRadiusProperty.radiusTopRight = topRight; borderRadiusProperty.radiusBottomLeft = bottomLeft; borderRadiusProperty.radiusBottomRight = bottomRight; borderRadiusProperty.multiValued = true; ParseImageAllBorderRadiusesResObj( borderRadiusProperty, topLeftResObj, topRightResObj, bottomLeftResObj, bottomRightResObj); ImageModel::GetInstance()->SetBorderRadius(borderRadiusProperty); ViewAbstractModel::GetInstance()->SetBorderRadius(borderRadiusProperty); } else { SetImageBorderRadius(topLeft, topRight, bottomLeft, bottomRight); } } } void JSImage::ParseResizableSlice(const JSRef& resizableObject) { ImageResizableSlice sliceResult; if (resizableObject->IsEmpty()) { ImageModel::GetInstance()->SetResizableSlice(sliceResult); return; } auto sliceValue = resizableObject->GetProperty("slice"); if (!sliceValue->IsObject()) { ImageModel::GetInstance()->SetResizableSlice(sliceResult); return; } JSRef sliceObj = JSRef::Cast(sliceValue); if (sliceObj->IsEmpty()) { ImageModel::GetInstance()->SetResizableSlice(sliceResult); return; } UpdateSliceResult(sliceObj, sliceResult); ImageModel::GetInstance()->SetResizableSlice(sliceResult); } void JSImage::ParseResizableLattice(const JSRef& resizableObject) { auto latticeValue = resizableObject->GetProperty("lattice"); if (latticeValue->IsUndefined() || latticeValue->IsNull()) { ImageModel::GetInstance()->ResetResizableLattice(); } CHECK_NULL_VOID(latticeValue->IsObject()); auto drawingLattice = CreateDrawingLattice(latticeValue); if (drawingLattice) { ImageModel::GetInstance()->SetResizableLattice(drawingLattice); } else { ImageModel::GetInstance()->ResetResizableLattice(); } } void JSImage::JsImageResizable(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } auto infoObj = info[0]; if (!infoObj->IsObject()) { auto slice = ImageResizableSlice(); ImageModel::GetInstance()->SetResizableSlice(slice); return; } JSRef resizableObject = JSRef::Cast(infoObj); ParseResizableSlice(resizableObject); ParseResizableLattice(resizableObject); } void ApplySliceResource(ImageResizableSlice& sliceResult, const std::string& resKey, const RefPtr& resObj, BorderImageDirection direction) { if (resObj && SystemProperties::ConfigChangePerform()) { sliceResult.AddResource( resKey, resObj, [direction](const RefPtr& currentResObj, ImageResizableSlice& slice) { CalcDimension dim; ResizableOption resizableOption; switch (direction) { case BorderImageDirection::LEFT: resizableOption = ResizableOption::LEFT; break; case BorderImageDirection::RIGHT: resizableOption = ResizableOption::RIGHT; break; case BorderImageDirection::TOP: resizableOption = ResizableOption::TOP; break; case BorderImageDirection::BOTTOM: resizableOption = ResizableOption::BOTTOM; break; default: return; } if (ResourceParseUtils::ParseResDimensionVpNG(currentResObj, dim) && dim.IsValid()) { slice.SetEdgeSlice(resizableOption, dim); } }); } } void JSImage::UpdateSliceResult(const JSRef& sliceObj, ImageResizableSlice& sliceResult) { // creatge a array has 4 elements for paresing sliceSize static std::array keys = { static_cast(ArkUIIndex::LEFT), static_cast(ArkUIIndex::RIGHT), static_cast(ArkUIIndex::TOP), static_cast(ArkUIIndex::BOTTOM) }; for (uint32_t i = 0; i < keys.size(); i++) { auto sliceSize = sliceObj->GetProperty(keys.at(i)); CalcDimension sliceDimension; RefPtr resObj; std::string resKey = "image."; if (!ParseJsDimensionVp(sliceSize, sliceDimension, resObj)) { continue; } if (!sliceDimension.IsValid()) { continue; } BorderImageDirection direction = static_cast(i); switch (direction) { case BorderImageDirection::LEFT: sliceResult.left = sliceDimension; resKey += "left"; break; case BorderImageDirection::RIGHT: sliceResult.right = sliceDimension; resKey += "right"; break; case BorderImageDirection::TOP: sliceResult.top = sliceDimension; resKey += "top"; break; case BorderImageDirection::BOTTOM: sliceResult.bottom = sliceDimension; resKey += "bottom"; break; default: break; } ApplySliceResource(sliceResult, resKey, resObj, direction); } ImageModel::GetInstance()->SetResizableSlice(sliceResult); } void JSImage::JsBorderRadius(const JSCallbackInfo& info) { if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) { JSViewAbstract::JsBorderRadius(info); ImageModel::GetInstance()->SetBackBorder(); return; } static std::vector checkList { JSCallbackInfoType::STRING, JSCallbackInfoType::NUMBER, JSCallbackInfoType::OBJECT }; auto jsVal = info[0]; if (!CheckJSCallbackInfo("JsBorderRadius", jsVal, checkList)) { ViewAbstractModel::GetInstance()->SetBorderRadius(Dimension {}); ImageModel::GetInstance()->SetBorderRadius(Dimension {}); return; } ParseBorderRadius(jsVal); } void JSImage::SetSourceSize(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } ImageModel::GetInstance()->SetImageSourceSize(JSViewAbstract::ParseSize(info)); } bool JSImage::ParseColorContent(const JSRef& jsValue) { if (jsValue.IsEmpty() || jsValue->IsNull() || !jsValue->IsObject()) { return false; } auto paramObject = JSRef::Cast(jsValue); JSRef typeVal = paramObject->GetProperty("colorContent_"); return !typeVal.IsEmpty() && typeVal->IsString() && typeVal->ToString() == "ORIGIN"; } void JSImage::SetImageFill(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } if (info.Length() < 1) { return; } Color color; RefPtr resObj; bool status = ParseJsColor(info[0], color, resObj); if (!status) { if (ParseColorContent(info[0])) { ImageModel::GetInstance()->ResetImageFill(); return; } if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_ELEVEN)) { return; } auto pipelineContext = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(pipelineContext); auto theme = pipelineContext->GetTheme(); CHECK_NULL_VOID(theme); color = theme->GetFillColor(); } ImageModel::GetInstance()->SetImageFill(color); // Fix the svg collision bug with the foreground color placeholder 0x00000001. ViewAbstractModel::GetInstance()->SetForegroundColor(Color::FOREGROUND); if (SystemProperties::ConfigChangePerform()) { ImageModel::GetInstance()->CreateWithResourceObj(ImageResourceType::FILL_COLOR, resObj); ImageModel::GetInstance()->SetImageFillSetByUser(!status); } } void JSImage::SetImageRenderMode(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } if (info.Length() < 1) { ImageModel::GetInstance()->SetImageRenderMode(ImageRenderMode::ORIGINAL); return; } auto jsImageRenderMode = info[0]; if (jsImageRenderMode->IsNumber()) { auto renderMode = static_cast(jsImageRenderMode->ToNumber()); if (renderMode < ImageRenderMode::ORIGINAL || renderMode > ImageRenderMode::TEMPLATE) { renderMode = ImageRenderMode::ORIGINAL; } ImageModel::GetInstance()->SetImageRenderMode(renderMode); } else { ImageModel::GetInstance()->SetImageRenderMode(ImageRenderMode::ORIGINAL); } } void JSImage::SetImageInterpolation(int32_t imageInterpolation) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } auto interpolation = static_cast(imageInterpolation); if (interpolation < ImageInterpolation::NONE || interpolation > ImageInterpolation::HIGH) { interpolation = ImageInterpolation::NONE; } ImageModel::GetInstance()->SetImageInterpolation(interpolation); } void JSImage::SetImageRepeat(int32_t imageRepeat) { auto repeat = static_cast(imageRepeat); if (repeat < ImageRepeat::NO_REPEAT || repeat > ImageRepeat::REPEAT) { repeat = ImageRepeat::NO_REPEAT; } ImageModel::GetInstance()->SetImageRepeat(repeat); } void JSImage::JsTransition(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->IsSrcSvgImage()) { JSViewAbstract::JsTransition(info); } else { JSViewAbstract::JsTransitionPassThrough(info); } } void JSImage::JsOpacity(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->IsSrcSvgImage()) { JSViewAbstract::JsOpacity(info); } else { JSViewAbstract::JsOpacityPassThrough(info); } } void JSImage::JsBlur(const JSCallbackInfo& info) { // only flutter runs special image blur #ifdef ENABLE_ROSEN_BACKEND JSViewAbstract::JsBlur(info); #else if (info.Length() < 1) { return; } double blur = 0.0; if (ParseJsDouble(info[0], blur)) { ImageModel::GetInstance()->SetBlur(blur); } #endif } void JSImage::SetAutoResize(bool autoResize) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } ImageModel::GetInstance()->SetAutoResize(autoResize); } void JSImage::SetSyncLoad(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } if (info.Length() < 1) { return; } auto tmpInfo = info[0]; if (!tmpInfo->IsBoolean()) { return; } ImageModel::GetInstance()->SetSyncMode(tmpInfo->ToBoolean()); } void JSColorFilter::ConstructorCallback(const JSCallbackInfo& args) { if (args.Length() < 1) { return; } auto tmpInfo = args[0]; if (!tmpInfo->IsArray()) { return; } JSRef array = JSRef::Cast(tmpInfo); if (array->Length() != COLOR_FILTER_MATRIX_SIZE) { return; } auto jscolorfilter = Referenced::MakeRefPtr(); if (jscolorfilter == nullptr) { return; } std::vector colorfilter; for (size_t i = 0; i < array->Length(); i++) { JSRef value = array->GetValueAt(i); if (value->IsNumber()) { colorfilter.emplace_back(value->ToNumber()); } } if (colorfilter.size() != COLOR_FILTER_MATRIX_SIZE) { return; } jscolorfilter->SetColorFilterMatrix(std::move(colorfilter)); jscolorfilter->IncRefCount(); args.SetReturnValue(Referenced::RawPtr(jscolorfilter)); } void JSColorFilter::DestructorCallback(JSColorFilter* obj) { if (obj != nullptr) { obj->DecRefCount(); } } void JSImage::SetColorFilter(const JSCallbackInfo& info) { if (info.Length() != 1) { ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } auto tmpInfo = info[0]; if (!tmpInfo->IsArray() && !tmpInfo->IsObject()) { ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } if (tmpInfo->IsObject() && !tmpInfo->IsArray()) { auto drawingColorFilter = CreateDrawingColorFilter(tmpInfo); if (drawingColorFilter) { ImageModel::GetInstance()->SetDrawingColorFilter(drawingColorFilter); return; } JSColorFilter* colorFilter; if (!tmpInfo->IsUndefined() && !tmpInfo->IsNull()) { colorFilter = JSRef::Cast(tmpInfo)->Unwrap(); } else { ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } if (colorFilter && colorFilter->GetColorFilterMatrix().size() == COLOR_FILTER_MATRIX_SIZE) { ImageModel::GetInstance()->SetColorFilterMatrix(colorFilter->GetColorFilterMatrix()); return; } ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } JSRef array = JSRef::Cast(tmpInfo); if (array->Length() != COLOR_FILTER_MATRIX_SIZE) { ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } std::vector colorfilter; for (size_t i = 0; i < array->Length(); i++) { JSRef value = array->GetValueAt(i); if (value->IsNumber()) { colorfilter.emplace_back(value->ToNumber()); } else { ImageModel::GetInstance()->SetColorFilterMatrix(DEFAULT_COLORFILTER_MATRIX); return; } } ImageModel::GetInstance()->SetColorFilterMatrix(colorfilter); } void JSImage::SetSmoothEdge(const JSCallbackInfo& info) { if (info.Length() != 1) { ImageModel::GetInstance()->SetSmoothEdge(DEFAULT_SMOOTHEDGE_VALUE); return; } double parseRes = DEFAULT_SMOOTHEDGE_VALUE; ParseJsDouble(info[0], parseRes); // Effective range : (FLOOR_SMOOTHEDGE_VALUE, CEIL_SMOOTHEDGE_VALUE] // otherwise: DEFAULT_SMOOTHEDGE_VALUE if (GreatNotEqual(parseRes, CEIL_SMOOTHEDGE_VALUE) || LessNotEqual(parseRes, FLOOR_SMOOTHEDGE_VALUE)) { parseRes = DEFAULT_SMOOTHEDGE_VALUE; } ImageModel::GetInstance()->SetSmoothEdge(static_cast(parseRes)); } void JSImage::SetDynamicRangeMode(const JSCallbackInfo& info) { if (info.Length() < 1) { ImageModel::GetInstance()->SetDynamicRangeMode(DynamicRangeMode::STANDARD); return; } int32_t parseRes = static_cast(DynamicRangeMode::STANDARD); ParseJsInteger(info[0], parseRes); if (parseRes < static_cast(DynamicRangeMode::HIGH) || parseRes > static_cast(DynamicRangeMode::STANDARD)) { parseRes = static_cast(DynamicRangeMode::STANDARD); } DynamicRangeMode dynamicRangeMode = static_cast(parseRes); ImageModel::GetInstance()->SetDynamicRangeMode(dynamicRangeMode); } void JSImage::SetHdrBrightness(const JSCallbackInfo& info) { float hdrBrightness = DEFAULT_HDR_BRIGHTNESS; if (info[0]->IsNumber()) { auto value = info[0]->ToNumber(); if (GreatOrEqual(value, HDR_BRIGHTNESS_MIN) && LessOrEqual(value, HDR_BRIGHTNESS_MAX)) { hdrBrightness = value; } } ImageModel::GetInstance()->SetHdrBrightness(hdrBrightness); } void JSImage::SetEnhancedImageQuality(const JSCallbackInfo& info) { if (info.Length() < 1) { ImageModel::GetInstance()->SetEnhancedImageQuality(AIImageQuality::LOW); return; } int32_t parseRes = static_cast(AIImageQuality::LOW); ParseJsInteger(info[0], parseRes); if (parseRes < static_cast(AIImageQuality::LOW) || parseRes > static_cast(AIImageQuality::HIGH)) { parseRes = static_cast(AIImageQuality::LOW); } AIImageQuality resolutionQuality = static_cast(parseRes); ImageModel::GetInstance()->SetEnhancedImageQuality(resolutionQuality); } void JSImage::SetOrientation(const JSCallbackInfo& info) { if (info.Length() < 1) { ImageModel::GetInstance()->SetOrientation(ImageRotateOrientation::UP); return; } int32_t parseRes = 0; ParseJsInteger(info[0], parseRes); if (parseRes < static_cast(ImageRotateOrientation::AUTO) || parseRes > static_cast(ImageRotateOrientation::LEFT_MIRRORED)) { parseRes = static_cast(ImageRotateOrientation::UP); } auto res = static_cast(parseRes); ImageModel::GetInstance()->SetOrientation(res); } void JSImage::CreateImageAnimation(std::vector>& pixelMaps, int32_t duration, int32_t iterations) { std::vector imageList; for (int i = 0; i < static_cast(pixelMaps.size()); i++) { ImageProperties image; image.pixelMap = pixelMaps[i]; imageList.push_back(image); } ImageModel::GetInstance()->CreateAnimation(imageList, duration, iterations); } void JSImage::JSBind(BindingTarget globalObj) { JSClass::Declare("Image"); MethodOptions opt = MethodOptions::NONE; JSClass::StaticMethod("create", &JSImage::Create, opt); JSClass::StaticMethod("alt", &JSImage::SetAlt, opt); JSClass::StaticMethod("objectFit", &JSImage::SetObjectFit, opt); JSClass::StaticMethod("imageMatrix", &JSImage::SetImageMatrix, opt); JSClass::StaticMethod("matchTextDirection", &JSImage::SetMatchTextDirection, opt); JSClass::StaticMethod("fitOriginalSize", &JSImage::SetFitOriginalSize, opt); JSClass::StaticMethod("sourceSize", &JSImage::SetSourceSize, opt); JSClass::StaticMethod("fillColor", &JSImage::SetImageFill, opt); JSClass::StaticMethod("renderMode", &JSImage::SetImageRenderMode, opt); JSClass::StaticMethod("objectRepeat", &JSImage::SetImageRepeat, opt); JSClass::StaticMethod("interpolation", &JSImage::SetImageInterpolation, opt); JSClass::StaticMethod("colorFilter", &JSImage::SetColorFilter, opt); JSClass::StaticMethod("edgeAntialiasing", &JSImage::SetSmoothEdge, opt); JSClass::StaticMethod("dynamicRangeMode", &JSImage::SetDynamicRangeMode, opt); JSClass::StaticMethod("hdrBrightness", &JSImage::SetHdrBrightness, opt); JSClass::StaticMethod("enhancedImageQuality", &JSImage::SetEnhancedImageQuality, opt); JSClass::StaticMethod("orientation", &JSImage::SetOrientation, opt); JSClass::StaticMethod("border", &JSImage::JsBorder); JSClass::StaticMethod("borderRadius", &JSImage::JsBorderRadius); JSClass::StaticMethod("onAttach", &JSInteractableView::JsOnAttach); JSClass::StaticMethod("onAppear", &JSInteractableView::JsOnAppear); JSClass::StaticMethod("onDetach", &JSInteractableView::JsOnDetach); JSClass::StaticMethod("onDisAppear", &JSInteractableView::JsOnDisAppear); JSClass::StaticMethod("autoResize", &JSImage::SetAutoResize); JSClass::StaticMethod("resizable", &JSImage::JsImageResizable); JSClass::StaticMethod("onTouch", &JSInteractableView::JsOnTouch); JSClass::StaticMethod("onHover", &JSInteractableView::JsOnHover); JSClass::StaticMethod("onKeyEvent", &JSInteractableView::JsOnKey); JSClass::StaticMethod("onDeleteEvent", &JSInteractableView::JsOnDelete); JSClass::StaticMethod("onClick", &JSInteractableView::JsOnClick); JSClass::StaticMethod("onComplete", &JSImage::OnComplete); JSClass::StaticMethod("onError", &JSImage::OnError); JSClass::StaticMethod("onFinish", &JSImage::OnFinish); JSClass::StaticMethod("syncLoad", &JSImage::SetSyncLoad); JSClass::StaticMethod("remoteMessage", &JSInteractableView::JsCommonRemoteMessage); JSClass::StaticMethod("draggable", &JSImage::JsSetDraggable); JSClass::StaticMethod("onDragStart", &JSImage::JsOnDragStart); JSClass::StaticMethod("copyOption", &JSImage::SetCopyOption); JSClass::StaticMethod("enableAnalyzer", &JSImage::EnableAnalyzer); JSClass::StaticMethod("analyzerConfig", &JSImage::AnalyzerConfig); // override method JSClass::StaticMethod("opacity", &JSImage::JsOpacity); JSClass::StaticMethod("blur", &JSImage::JsBlur); JSClass::StaticMethod("transition", &JSImage::JsTransition); JSClass::StaticMethod("pointLight", &JSViewAbstract::JsPointLight, opt); JSClass::InheritAndBind(globalObj); JSClass::Declare("ColorFilter"); JSClass::Bind(globalObj, JSColorFilter::ConstructorCallback, JSColorFilter::DestructorCallback); } void JSImage::JsSetDraggable(const JSCallbackInfo& info) { bool draggable = Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN); if (info.Length() > 0 && info[0]->IsBoolean()) { draggable = info[0]->ToBoolean(); } ImageModel::GetInstance()->SetDraggable(draggable); } void JSImage::JsOnDragStart(const JSCallbackInfo& info) { if (!Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FIFTEEN)) { JSViewAbstract::JsOnDragStart(info); return; } if (info.Length() != 1 || !info[0]->IsFunction()) { return; } RefPtr jsOnDragStartFunc = AceType::MakeRefPtr(JSRef::Cast(info[0])); WeakPtr frameNode = AceType::WeakClaim(NG::ViewStackProcessor::GetInstance()->GetMainFrameNode()); auto onDragStartId = [execCtx = info.GetExecutionContext(), func = std::move(jsOnDragStartFunc), node = frameNode]( const RefPtr& info, const std::string& extraParams) -> NG::DragDropBaseInfo { NG::DragDropBaseInfo itemInfo; JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx, itemInfo); PipelineContext::SetCallBackNode(node); auto ret = func->Execute(info, extraParams); if (!ret->IsObject()) { return itemInfo; } if (ParseAndUpdateDragItemInfo(ret, itemInfo)) { return itemInfo; } auto builderObj = JSRef::Cast(ret); #if defined(PIXEL_MAP_SUPPORTED) auto pixmap = builderObj->GetProperty("pixelMap"); itemInfo.pixelMap = CreatePixelMapFromNapiValue(pixmap); #endif auto extraInfo = builderObj->GetProperty("extraInfo"); ParseJsString(extraInfo, itemInfo.extraInfo); ParseAndUpdateDragItemInfo(builderObj->GetProperty("builder"), itemInfo); return itemInfo; }; ImageModel::GetInstance()->SetOnDragStart(std::move(onDragStartId)); } void JSImage::SetCopyOption(const JSCallbackInfo& info) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } auto copyOptions = CopyOptions::None; if (info[0]->IsNumber()) { auto enumNumber = info[0]->ToNumber(); copyOptions = static_cast(enumNumber); if (copyOptions < CopyOptions::None || copyOptions > CopyOptions::Distributed) { copyOptions = CopyOptions::None; } } ImageModel::GetInstance()->SetCopyOption(copyOptions); } void JSImage::EnableAnalyzer(bool isEnableAnalyzer) { if (ImageModel::GetInstance()->GetIsAnimation()) { return; } ImageModel::GetInstance()->EnableAnalyzer(isEnableAnalyzer); } void JSImage::AnalyzerConfig(const JSCallbackInfo& info) { auto configParams = info[0]; if (configParams->IsNull() || !configParams->IsObject()) { return; } auto engine = EngineHelper::GetCurrentEngine(); CHECK_NULL_VOID(engine); NativeEngine* nativeEngine = engine->GetNativeEngine(); CHECK_NULL_VOID(nativeEngine); panda::Local value = configParams.Get().GetLocalHandle(); JSValueWrapper valueWrapper = value; ScopeRAII scope(reinterpret_cast(nativeEngine)); napi_value nativeValue = nativeEngine->ValueToNapiValue(valueWrapper); ImageModel::GetInstance()->SetImageAnalyzerConfig(nativeValue); // As an example, the function is not in effect. auto paramObject = JSRef::Cast(configParams); JSRef typeVal = paramObject->GetProperty("types"); ImageAnalyzerConfig analyzerConfig; if (typeVal->IsArray()) { auto array = JSRef::Cast(typeVal); std::set types; for (size_t i = 0; i < array->Length(); ++i) { if (!array->GetValueAt(i)->IsNumber()) { continue; } int value = array->GetValueAt(i)->ToNumber(); ImageAnalyzerType type = static_cast(value); if (type != ImageAnalyzerType::SUBJECT && type != ImageAnalyzerType::TEXT) { continue; } types.insert(type); } analyzerConfig.types = std::move(types); } ImageModel::GetInstance()->SetImageAnalyzerConfig(analyzerConfig); } } // namespace OHOS::Ace::Framework