From cd79bc31946966ca6fb34433b3449fbf506947ad Mon Sep 17 00:00:00 2001 From: zhanghang Date: Thu, 11 Sep 2025 14:36:06 +0800 Subject: [PATCH] =?UTF-8?q?Image=E7=BB=84=E4=BB=B6alt=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A0=E8=BD=BD=E5=A4=B1=E8=B4=A5=E6=97=B6?= =?UTF-8?q?=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: zhanghang --- .../declarative_frontend/jsview/js_image.cpp | 82 +++++++++++++- .../declarative_frontend/jsview/js_image.h | 4 + .../jsview/models/image_model_impl.h | 1 + .../pattern/image/image_layout_property.h | 1 + .../components_ng/pattern/image/image_model.h | 1 + .../pattern/image/image_model_ng.cpp | 5 + .../pattern/image/image_model_ng.h | 1 + .../pattern/image/image_pattern.cpp | 103 +++++++++++++++++- .../pattern/image/image_pattern.h | 11 ++ 9 files changed, 206 insertions(+), 3 deletions(-) diff --git a/frameworks/bridge/declarative_frontend/jsview/js_image.cpp b/frameworks/bridge/declarative_frontend/jsview/js_image.cpp index 85a420a59bd..eaa770c1496 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_image.cpp +++ b/frameworks/bridge/declarative_frontend/jsview/js_image.cpp @@ -59,6 +59,8 @@ 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 int ALT_PLACEHOLDER_CODE = 1; +constexpr int ALT_ERROR_CODE = 2; constexpr uint32_t FIT_MATRIX = 16; constexpr char DRAWABLE_DESCRIPTOR_NAME[] = "DrawableDescriptor"; constexpr char LAYERED_DRAWABLE_DESCRIPTOR_NAME[] = "LayeredDrawableDescriptor"; @@ -143,11 +145,14 @@ void JSImage::SetAlt(const JSCallbackInfo& args) if (args.Length() < 1) { return; } - auto context = PipelineBase::GetCurrentContext(); CHECK_NULL_VOID(context); bool isCard = context->IsFormRender(); - + if (args[0]->IsObject() && IsImageAltObject(args[0])) { + JSRef jsObj = JSRef::Cast(args[0]); + ParseImageAltType(jsObj, isCard); + return; + } std::string src; bool srcValid = false; RefPtr resObj; @@ -1265,4 +1270,77 @@ void JSImage::SetContentTransition(const JSCallbackInfo& info) ImageModel::GetInstance()->SetContentTransition(ContentTransitionType::IDENTITY); } } + +void JSImage::ParseAltImageAlt(JSRef val, bool isCard, int type) +{ + std::string src; + bool srcValid = false; + RefPtr resObj; + if (val->IsString()) { + src = val->ToString(); + } else { + srcValid = ParseJsMedia(val, src, resObj); + } + + if (ImageSourceInfo::ResolveURIType(src) == SrcType::NETWORK) { + return; + } + int32_t resId = 0; + if (val->IsObject() && !IsImageAltObject(val)) { + JSRef jsObj = JSRef::Cast(val); + JSRef tmp = jsObj->GetProperty("id"); + if (!tmp->IsNull() && tmp->IsNumber()) { + resId = tmp->ToNumber(); + } + } + std::string bundleName; + std::string moduleName; + GetJsMediaBundleInfo(val, bundleName, moduleName); + RefPtr pixmap = nullptr; + + if (!srcValid && !isCard) { +#if defined(PIXEL_MAP_SUPPORTED) + pixmap = CreatePixelMapFromNapiValue(val); +#endif + } + auto srcRef = std::make_shared(src); + auto srcInfo = CreateSourceInfo(srcRef, pixmap, bundleName, moduleName); + srcInfo.SetIsUriPureNumber((resId == -1)); + switch (type) { + case ALT_PLACEHOLDER_CODE: + ImageModel::GetInstance()->SetAlt(srcInfo); + break; + case ALT_ERROR_CODE: + ImageModel::GetInstance()->SetAltError(srcInfo); + break; + default: + break; + } + if (SystemProperties::ConfigChangePerform()) { + ImageModel::GetInstance()->CreateWithResourceObj(ImageResourceType::ALT, resObj); + } +} + +void ParseImageAltType(JSRef jsObj, bool isCard) +{ + if (jsObj->HasProperty("placeholder")) { + JSRef placeholderVal = jsObj->GetProperty("placeholder"); + ParseAltImageAlt(placeholderVal, isCard, ALT_PLACEHOLDER_CODE); + } + + if (jsObj->HasProperty("error")) { + JSRef errorVal = jsObj->GetProperty("error"); + ParseAltImageAlt(errorVal, isCard, ALT_ERROR_CODE); + } +} + +bool JSImage::IsImageAltObject(JSRef val) +{ + if (!val->IsObject()) { + return false; + } + + JSRef jsObj = JSRef::Cast(val); + return jsObj->HasProperty("placeholder") || jsObj->HasProperty("error"); +} } // namespace OHOS::Ace::Framework diff --git a/frameworks/bridge/declarative_frontend/jsview/js_image.h b/frameworks/bridge/declarative_frontend/jsview/js_image.h index 03986d9e54e..8ba25e4cfe9 100644 --- a/frameworks/bridge/declarative_frontend/jsview/js_image.h +++ b/frameworks/bridge/declarative_frontend/jsview/js_image.h @@ -58,6 +58,7 @@ public: static void SetHdrBrightness(const JSCallbackInfo& info); static void SetEnhancedImageQuality(const JSCallbackInfo& info); static void SetOrientation(const JSCallbackInfo& info); + static void SetAltError(const JSCallbackInfo& args); static void JsBorder(const JSCallbackInfo& info); static void JsBorderRadius(const JSCallbackInfo& info); @@ -84,6 +85,9 @@ public: static ImageType ParseImageType(const JSRef& jsValue); static bool ParseContentTransitionEffect(const JSRef& jsValue, ContentTransitionType& contentTransitionType); static void SetContentTransition(const JSCallbackInfo& info); + static bool IsImageAltObject(JSRef val); + static void ParseImageAltType(JSRef jsObj, bool isCard); + static void ParseAltImageAlt(JSRef val, bool isCard, int type); protected: static void SetBorder(const Border& border); diff --git a/frameworks/bridge/declarative_frontend/jsview/models/image_model_impl.h b/frameworks/bridge/declarative_frontend/jsview/models/image_model_impl.h index bfe179aaadb..134b11d4662 100644 --- a/frameworks/bridge/declarative_frontend/jsview/models/image_model_impl.h +++ b/frameworks/bridge/declarative_frontend/jsview/models/image_model_impl.h @@ -80,6 +80,7 @@ public: void SetImageFillSetByUser(bool value) override {}; void SetSupportSvg2(bool enable) override {}; void SetContentTransition(ContentTransitionType contentTransition) override {}; + void SetAltError(const ImageSourceInfo& src) override{}; }; } // namespace OHOS::Ace::Framework diff --git a/frameworks/core/components_ng/pattern/image/image_layout_property.h b/frameworks/core/components_ng/pattern/image/image_layout_property.h index d2ce39a223a..39f53fc49a0 100644 --- a/frameworks/core/components_ng/pattern/image/image_layout_property.h +++ b/frameworks/core/components_ng/pattern/image/image_layout_property.h @@ -90,6 +90,7 @@ public: ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(BaselineOffset, Dimension, PROPERTY_UPDATE_MEASURE_SELF_AND_PARENT); ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ImageRotateOrientation, ImageRotateOrientation, PROPERTY_UPDATE_MEASURE); ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(ImageFillSetByUser, bool, PROPERTY_UPDATE_MEASURE); + ACE_DEFINE_PROPERTY_ITEM_WITHOUT_GROUP(AltError, ImageSourceInfo, PROPERTY_UPDATE_NORMAL); private: ACE_DISALLOW_COPY_AND_MOVE(ImageLayoutProperty); diff --git a/frameworks/core/components_ng/pattern/image/image_model.h b/frameworks/core/components_ng/pattern/image/image_model.h index 6c55d85ab0a..55a1efd29b1 100644 --- a/frameworks/core/components_ng/pattern/image/image_model.h +++ b/frameworks/core/components_ng/pattern/image/image_model.h @@ -109,6 +109,7 @@ public: virtual void SetImageFillSetByUser(bool value) = 0; virtual void SetSupportSvg2(bool enable) = 0; virtual void SetContentTransition(ContentTransitionType contentTransition) = 0; + virtual void SetAltError(const ImageSourceInfo& src) = 0; private: static std::unique_ptr instance_; diff --git a/frameworks/core/components_ng/pattern/image/image_model_ng.cpp b/frameworks/core/components_ng/pattern/image/image_model_ng.cpp index 00cf6b3fcf0..97d425f991c 100644 --- a/frameworks/core/components_ng/pattern/image/image_model_ng.cpp +++ b/frameworks/core/components_ng/pattern/image/image_model_ng.cpp @@ -1281,5 +1281,10 @@ ContentTransitionType ImageModelNG::GetContentTransition(FrameNode* frameNode) CHECK_NULL_RETURN(paintProperty->GetImagePaintStyle(), ContentTransitionType::IDENTITY); return paintProperty->GetImagePaintStyle()->GetContentTransition().value_or(ContentTransitionType::IDENTITY); } + +void ImageModelNG::SetAltError(const ImageSourceInfo& src) +{ + ACE_UPDATE_LAYOUT_PROPERTY(ImageLayoutProperty, AltError, src); +} } // namespace OHOS::Ace::NG #endif // FOUNDATION_ACE_FRAMEWORKS_CORE_COMPONENTS_NG_PATTERN_IMAGE_IMAGE_MODEL_NG_CPP diff --git a/frameworks/core/components_ng/pattern/image/image_model_ng.h b/frameworks/core/components_ng/pattern/image/image_model_ng.h index a2aad86680c..ecab65404e0 100644 --- a/frameworks/core/components_ng/pattern/image/image_model_ng.h +++ b/frameworks/core/components_ng/pattern/image/image_model_ng.h @@ -81,6 +81,7 @@ public: void SetImageFillSetByUser(bool value) override; void SetSupportSvg2(bool enable) override; void SetContentTransition(ContentTransitionType contentTransition) override; + void SetAltError(const ImageSourceInfo& src) override; static RefPtr CreateFrameNode(int32_t nodeId, const std::string& src, RefPtr& pixMap, const std::string& bundleName, const std::string& moduleName, bool isUriPureNumber = false); static void InitImage(FrameNode* frameNode, std::string& src); diff --git a/frameworks/core/components_ng/pattern/image/image_pattern.cpp b/frameworks/core/components_ng/pattern/image/image_pattern.cpp index f59c41860d7..b1b2f87a635 100644 --- a/frameworks/core/components_ng/pattern/image/image_pattern.cpp +++ b/frameworks/core/components_ng/pattern/image/image_pattern.cpp @@ -378,6 +378,11 @@ void ImagePattern::ClearAltData() altImage_ = nullptr; altDstRect_.reset(); altSrcRect_.reset(); + + altErrorCtx_ = nullptr; + altErrorImage_ = nullptr; + altErrorDstRect_.reset(); + altErrorSrcRect_.reset(); } void ImagePattern::ApplyAIModificationsToImage() @@ -463,6 +468,7 @@ void ImagePattern::OnImageLoadSuccess() context->SetColorGamut(pixelMap->GetInnerColorGamut()); } ReportPerfData(host, IMAGE_LOAD_SUCCESS); + loadSuccess_ = true; /* * Trigger the completion callback. Since the callback is executed externally and its behavior * is not controlled here, it may lead to object mutation or destruction. Therefore, avoid @@ -614,6 +620,12 @@ void ImagePattern::OnImageLoadFail(const std::string& errorMsg, const ImageError const auto& geometryNode = host->GetGeometryNode(); auto imageEventHub = GetEventHub(); CHECK_NULL_VOID(imageEventHub); + auto imageLayoutProperty = GetLayoutProperty(); + CHECK_NULL_VOID(imageLayoutProperty); + if (imageLayoutProperty->GetAltError()) { + auto altErrorImageSourceInfo = imageLayoutProperty->GetAltError().value_or(ImageSourceInfo("")); + LoadAltErrorImage(altErrorImageSourceInfo); + } LoadImageFailEvent event( geometryNode->GetFrameSize().Width(), geometryNode->GetFrameSize().Height(), errorMsg, errorInfo); ReportPerfData(host, IMAGE_LOAD_FAIL); @@ -721,11 +733,16 @@ RefPtr ImagePattern::CreateNodePaintMethod() // Mark the rendering as successful on the instance. pattern->SetRenderedImageInfo(std::move(renderedImageInfo)); }; - if (image_) { + if (image_ && loadSuccess_) { image_->SetDrawCompleteCallback(std::move(drawCompleteCallback)); imagePaintMethod_->UpdatePaintMethod(image_, imagePaintMethodConfig); return imagePaintMethod_; } + if (altErrorImage_ && altErrorDstRect_ && altErrorSrcRect_) { + altErrorImage_->SetDrawCompleteCallback(std::move(drawCompleteCallback)); + imagePaintMethod_->UpdatePaintMethod(altErrorImage_, imagePaintMethodConfig); + return imagePaintMethod_; + } if (altImage_ && altDstRect_ && altSrcRect_) { altImage_->SetDrawCompleteCallback(std::move(drawCompleteCallback)); imagePaintMethod_->UpdatePaintMethod(altImage_, imagePaintMethodConfig); @@ -1273,6 +1290,8 @@ bool ImagePattern::RecycleImageData() image_ = nullptr; altLoadingCtx_ = nullptr; altImage_ = nullptr; + altErrorCtx_ = nullptr; + altErrorImage_ = nullptr; ACE_SCOPED_TRACE("OnRecycleImageData imageInfo: [%s]", imageDfxConfig_.ToStringWithSrc().c_str()); return true; } @@ -1292,6 +1311,8 @@ void ImagePattern::OnRecycle() image_ = nullptr; altLoadingCtx_ = nullptr; altImage_ = nullptr; + altErrorCtx_ = nullptr; + altErrorImage_ = nullptr; auto frameNode = GetHost(); CHECK_NULL_VOID(frameNode); @@ -2280,6 +2301,10 @@ void ImagePattern::ResetImageAndAlt() altImage_ = nullptr; altDstRect_.reset(); altSrcRect_.reset(); + altErrorCtx_ = nullptr; + altErrorImage_ = nullptr; + altErrorDstRect_.reset(); + altErrorSrcRect_.reset(); auto rsRenderContext = frameNode->GetRenderContext(); CHECK_NULL_VOID(rsRenderContext); rsRenderContext->RemoveContentModifier(contentMod_); @@ -2483,4 +2508,80 @@ ContentTransitionType ImagePattern::GetContentTransitionParam() auto contentTransition = paintProperty->GetContentTransition().value_or(ContentTransitionType::IDENTITY); return contentTransition; } + +void ImagePattern::LoadAltErrorImage(const ImageSourceInfo& altErrorImageSourceInfo) +{ + LoadNotifier altLoadNotifier(CreateDataReadyCallbackForAltError(), CreateLoadSuccessCallbackForAltError(), nullptr); + if (!altErrorCtx_ || altErrorCtx_->GetSourceInfo() != altErrorImageSourceInfo || + (altErrorCtx_ && altErrorImageSourceInfo.IsSvg())) { + altErrorImageDfxConfig_ = CreateImageDfxConfig(altErrorImageSourceInfo); + altErrorCtx_ = AceType::MakeRefPtr( + altErrorImageSourceInfo, std::move(altLoadNotifier), false, isSceneBoardWindow_, altErrorImageDfxConfig_); + CHECK_NULL_VOID(altErrorCtx_); + altErrorCtx_->LoadImageData(); + } +} + +DataReadyNotifyTask ImagePattern::CreateDataReadyCallbackForAltError() +{ + return [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) { + auto pattern = weak.Upgrade(); + CHECK_NULL_VOID(pattern); + CHECK_NULL_VOID(pattern->altErrorCtx_); + auto imageLayoutProperty = pattern->GetLayoutProperty(); + CHECK_NULL_VOID(imageLayoutProperty); + auto currentAltErrorSourceInfo = imageLayoutProperty->GetAltError().value_or(ImageSourceInfo("")); + if (currentAltErrorSourceInfo != sourceInfo) { + TAG_LOGW(AceLogTag::ACE_IMAGE, "alt src not match, %{public}s: %{private}s - %{private}s", + pattern->imageDfxConfig_.ToStringWithoutSrc().c_str(), currentAltErrorSourceInfo.ToString().c_str(), + sourceInfo.ToString().c_str()); + return; + } + auto host = pattern->GetHost(); + CHECK_NULL_VOID(host); + if (!host->IsActive()) { + return; + } + const auto& geometryNode = host->GetGeometryNode(); + CHECK_NULL_VOID(geometryNode); + if (!geometryNode->GetContent()) { + host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE); + return; + } + pattern->altErrorCtx_->MakeCanvasImageIfNeed( + geometryNode->GetContentSize(), true, imageLayoutProperty->GetImageFit().value_or(ImageFit::COVER)); + }; +} + +LoadSuccessNotifyTask ImagePattern::CreateLoadSuccessCallbackForAltError() +{ + return [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) { + auto pattern = weak.Upgrade(); + CHECK_NULL_VOID(pattern); + CHECK_NULL_VOID(pattern->altErrorCtx_); + auto layoutProps = pattern->GetLayoutProperty(); + CHECK_NULL_VOID(layoutProps); + auto currentAltSrc = layoutProps->GetAltError().value_or(ImageSourceInfo("")); + if (currentAltSrc != sourceInfo) { + TAG_LOGW(AceLogTag::ACE_IMAGE, "alt src not match, %{public}s: %{private}s - %{private}s", + pattern->imageDfxConfig_.ToStringWithoutSrc().c_str(), currentAltSrc.ToString().c_str(), + sourceInfo.ToString().c_str()); + return; + } + auto host = pattern->GetHost(); + CHECK_NULL_VOID(host); + pattern->loadSuccess_ = false; + host->MarkNeedRenderOnly(); + pattern->altErrorImage_ = pattern->altErrorCtx_->MoveCanvasImage(); + CHECK_NULL_VOID(pattern->altErrorImage_); + pattern->altErrorImage_->SetImageDfxConfig(pattern->altImageDfxConfig_); + pattern->altErrorDstRect_ = std::make_unique(pattern->altErrorCtx_->GetSrcRect()); + pattern->altErrorSrcRect_ = std::make_unique(pattern->altErrorCtx_->GetDstRect()); + pattern->SetImagePaintConfig(pattern->altErrorImage_, *pattern->altErrorSrcRect_, *pattern->altErrorDstRect_, + pattern->altErrorCtx_->GetSourceInfo(), pattern->altErrorCtx_->GetFrameCount()); + + pattern->PrepareAnimation(pattern->altErrorImage_); + host->MarkDirtyNode(PROPERTY_UPDATE_RENDER); + }; +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/image/image_pattern.h b/frameworks/core/components_ng/pattern/image/image_pattern.h index 25e344ead69..0ade7fdee18 100644 --- a/frameworks/core/components_ng/pattern/image/image_pattern.h +++ b/frameworks/core/components_ng/pattern/image/image_pattern.h @@ -442,6 +442,9 @@ private: LoadSuccessNotifyTask CreateLoadSuccessCallbackForAlt(); LoadFailNotifyTask CreateLoadFailCallbackForAlt(); + DataReadyNotifyTask CreateDataReadyCallbackForAltError(); + LoadSuccessNotifyTask CreateLoadSuccessCallbackForAltError(); + void OnColorConfigurationUpdate() override; void OnDirectionConfigurationUpdate() override; void OnIconConfigurationUpdate() override; @@ -463,6 +466,7 @@ private: void UpdateSvgSmoothEdgeValue(); void OnKeyEvent(const KeyEvent& event); void InitFromThemeIfNeed(); + void LoadAltErrorImage(const ImageSourceInfo& altErrorImageSourceInfo); private: RefPtr drawable_; @@ -485,6 +489,11 @@ private: std::unique_ptr altDstRect_; std::unique_ptr altSrcRect_; + RefPtr altErrorCtx_; + RefPtr altErrorImage_; + std::unique_ptr altErrorDstRect_; + std::unique_ptr altErrorSrcRect_; + RefPtr longPressEvent_; RefPtr clickEvent_; RefPtr mouseEvent_; @@ -493,6 +502,7 @@ private: std::shared_ptr imageAnalyzerManager_; ImageDfxConfig imageDfxConfig_; ImageDfxConfig altImageDfxConfig_; + ImageDfxConfig altErrorImageDfxConfig_; bool enableDrag_ = false; std::function keyEventCallback_ = nullptr; @@ -524,6 +534,7 @@ private: bool hasSetPixelMapMemoryName_ = false; bool previousVisibility_ = false; bool supportSvg2_ = false; + bool loadSuccess_ = false; ImageType imageType_ = ImageType::BASE; ACE_DISALLOW_COPY_AND_MOVE(ImagePattern); -- Gitee