• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/image/image_pattern.h"
17 
18 #include <array>
19 
20 #include "base/utils/utils.h"
21 #include "core/components/theme/icon_theme.h"
22 #include "core/components_ng/base/view_abstract.h"
23 #include "core/components_ng/pattern/image/image_paint_method.h"
24 #include "core/components_v2/inspector/inspector_constants.h"
25 #include "core/pipeline/base/element_register.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 #include "core/pipeline_ng/ui_task_scheduler.h"
28 
29 namespace OHOS::Ace::NG {
30 
CreateDataReadyCallback()31 DataReadyNotifyTask ImagePattern::CreateDataReadyCallback()
32 {
33     auto task = [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) {
34         auto pattern = weak.Upgrade();
35         CHECK_NULL_VOID(pattern);
36         auto imageLayoutProperty = pattern->GetLayoutProperty<ImageLayoutProperty>();
37         CHECK_NULL_VOID(imageLayoutProperty);
38         auto currentSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo(""));
39         if (currentSourceInfo != sourceInfo) {
40             LOGW("sourceInfo does not match, ignore current callback. current: %{public}s vs callback's: %{public}s",
41                 currentSourceInfo.ToString().c_str(), sourceInfo.ToString().c_str());
42             return;
43         }
44         LOGD("Image Data Ready %{private}s", sourceInfo.ToString().c_str());
45         pattern->OnImageDataReady();
46     };
47     return task;
48 }
49 
CreateLoadSuccessCallback()50 LoadSuccessNotifyTask ImagePattern::CreateLoadSuccessCallback()
51 {
52     auto task = [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) {
53         auto pattern = weak.Upgrade();
54         CHECK_NULL_VOID(pattern);
55         auto imageLayoutProperty = pattern->GetLayoutProperty<ImageLayoutProperty>();
56         CHECK_NULL_VOID(imageLayoutProperty);
57         auto currentSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo(""));
58         if (currentSourceInfo != sourceInfo) {
59             LOGW("sourceInfo does not match, ignore current callback. current: %{public}s vs callback's: %{public}s",
60                 currentSourceInfo.ToString().c_str(), sourceInfo.ToString().c_str());
61             return;
62         }
63         LOGD("Image Load Success %{private}s", sourceInfo.ToString().c_str());
64         pattern->OnImageLoadSuccess();
65     };
66     return task;
67 }
68 
CreateLoadFailCallback()69 LoadFailNotifyTask ImagePattern::CreateLoadFailCallback()
70 {
71     auto task = [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) {
72         auto pattern = weak.Upgrade();
73         CHECK_NULL_VOID(pattern);
74         auto imageLayoutProperty = pattern->GetLayoutProperty<ImageLayoutProperty>();
75         CHECK_NULL_VOID(imageLayoutProperty);
76         auto currentSourceInfo = imageLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo(""));
77         if (currentSourceInfo != sourceInfo) {
78             LOGW("sourceInfo does not match, ignore current callback. current: %{public}s vs callback's: %{public}s",
79                 currentSourceInfo.ToString().c_str(), sourceInfo.ToString().c_str());
80             return;
81         }
82         pattern->OnImageLoadFail();
83     };
84     return task;
85 }
86 
PrepareAnimation()87 void ImagePattern::PrepareAnimation()
88 {
89     if (image_->IsStatic()) {
90         return;
91     }
92     SetRedrawCallback();
93     RegisterVisibleAreaChange();
94 }
95 
SetRedrawCallback()96 void ImagePattern::SetRedrawCallback()
97 {
98     CHECK_NULL_VOID_NOLOG(image_);
99     // set animation flush function for svg / gif
100     image_->SetRedrawCallback([weak = WeakClaim(RawPtr(GetHost()))] {
101         auto imageNode = weak.Upgrade();
102         CHECK_NULL_VOID(imageNode);
103         imageNode->MarkNeedRenderOnly();
104     });
105 }
106 
RegisterVisibleAreaChange()107 void ImagePattern::RegisterVisibleAreaChange()
108 {
109     auto pipeline = PipelineContext::GetCurrentContext();
110     // register to onVisibleAreaChange
111     CHECK_NULL_VOID(pipeline);
112     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
113         auto self = weak.Upgrade();
114         CHECK_NULL_VOID(self);
115         LOGD("current image visible ratio = %f", ratio);
116         self->OnVisibleChange(visible);
117     };
118     auto host = GetHost();
119     CHECK_NULL_VOID(host);
120     // reset visibleAreaChangeNode
121     pipeline->RemoveVisibleAreaChangeNode(host->GetId());
122     pipeline->AddVisibleAreaChangeNode(host, 0.0f, callback);
123 }
124 
OnImageLoadSuccess()125 void ImagePattern::OnImageLoadSuccess()
126 {
127     auto host = GetHost();
128     CHECK_NULL_VOID(host);
129     const auto& geometryNode = host->GetGeometryNode();
130     CHECK_NULL_VOID(geometryNode);
131     auto imageEventHub = GetEventHub<ImageEventHub>();
132     CHECK_NULL_VOID(imageEventHub);
133     LoadImageSuccessEvent loadImageSuccessEvent_(loadingCtx_->GetImageSize().Width(),
134         loadingCtx_->GetImageSize().Height(), geometryNode->GetFrameSize().Width(),
135         geometryNode->GetFrameSize().Height(), 1);
136     imageEventHub->FireCompleteEvent(loadImageSuccessEvent_);
137     // update src data
138     image_ = loadingCtx_->MoveCanvasImage();
139     srcRect_ = loadingCtx_->GetSrcRect();
140     dstRect_ = loadingCtx_->GetDstRect();
141 
142     SetImagePaintConfig(image_, srcRect_, dstRect_, loadingCtx_->GetSourceInfo().IsSvg());
143     PrepareAnimation();
144     if (draggable_) {
145         EnableDrag();
146     }
147     // clear alt data
148     altLoadingCtx_ = nullptr;
149     altImage_ = nullptr;
150     altDstRect_.reset();
151     altSrcRect_.reset();
152     // TODO: only do paint task when the pattern is active
153     // figure out why here is always inactive
154     host->MarkNeedRenderOnly();
155 }
156 
OnImageDataReady()157 void ImagePattern::OnImageDataReady()
158 {
159     auto host = GetHost();
160     CHECK_NULL_VOID(host);
161     const auto& geometryNode = host->GetGeometryNode();
162     CHECK_NULL_VOID(geometryNode);
163     auto imageEventHub = GetEventHub<ImageEventHub>();
164     CHECK_NULL_VOID(imageEventHub);
165     LoadImageSuccessEvent loadImageSuccessEvent_(loadingCtx_->GetImageSize().Width(),
166         loadingCtx_->GetImageSize().Height(), geometryNode->GetFrameSize().Width(),
167         geometryNode->GetFrameSize().Height(), 0);
168     imageEventHub->FireCompleteEvent(loadImageSuccessEvent_);
169     if (!host->IsActive()) {
170         return;
171     }
172     if (!geometryNode->GetContent() || (geometryNode->GetContent() && altLoadingCtx_)) {
173         host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
174         return;
175     }
176     host->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
177 }
178 
OnImageLoadFail()179 void ImagePattern::OnImageLoadFail()
180 {
181     auto host = GetHost();
182     CHECK_NULL_VOID(host);
183     const auto& geometryNode = host->GetGeometryNode();
184     auto imageEventHub = GetEventHub<ImageEventHub>();
185     CHECK_NULL_VOID(imageEventHub);
186     LoadImageFailEvent loadImageFailEvent_(
187         geometryNode->GetFrameSize().Width(), geometryNode->GetFrameSize().Height(), "");
188     // TODO: remove errorMsg in fail event
189     imageEventHub->FireErrorEvent(std::move(loadImageFailEvent_));
190 }
191 
SetImagePaintConfig(const RefPtr<CanvasImage> & canvasImage,const RectF & srcRect,const RectF & dstRect,bool isSvg)192 void ImagePattern::SetImagePaintConfig(
193     const RefPtr<CanvasImage>& canvasImage, const RectF& srcRect, const RectF& dstRect, bool isSvg)
194 {
195     auto layoutProps = GetLayoutProperty<ImageLayoutProperty>();
196     CHECK_NULL_VOID(layoutProps);
197 
198     ImagePaintConfig config {
199         .srcRect_ = srcRect,
200         .dstRect_ = dstRect,
201     };
202     config.imageFit_ = layoutProps->GetImageFit().value_or(ImageFit::COVER);
203     config.isSvg_ = isSvg;
204 
205     canvasImage->SetPaintConfig(config);
206 }
207 
CreateNodePaintMethod()208 RefPtr<NodePaintMethod> ImagePattern::CreateNodePaintMethod()
209 {
210     if (image_) {
211         return MakeRefPtr<ImagePaintMethod>(image_);
212     }
213     if (altImage_ && altDstRect_ && altSrcRect_) {
214         return MakeRefPtr<ImagePaintMethod>(altImage_);
215     }
216     return nullptr;
217 }
218 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)219 bool ImagePattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
220 {
221     if (config.skipMeasure || dirty->SkipMeasureContent()) {
222         return false;
223     }
224     return image_;
225 }
226 
LoadImageDataIfNeed()227 void ImagePattern::LoadImageDataIfNeed()
228 {
229     auto imageLayoutProperty = GetLayoutProperty<ImageLayoutProperty>();
230     CHECK_NULL_VOID(imageLayoutProperty);
231     auto imageRenderProperty = GetPaintProperty<ImageRenderProperty>();
232     CHECK_NULL_VOID(imageRenderProperty);
233     auto src = imageLayoutProperty->GetImageSourceInfo().value_or(ImageSourceInfo(""));
234     UpdateInternalResource(src);
235     std::optional<Color> svgFillColorOpt = std::nullopt;
236     if (src.IsSvg()) {
237         svgFillColorOpt = src.GetFillColor();
238     }
239 
240     if (!loadingCtx_ || loadingCtx_->GetSourceInfo() != src ||
241         (src.IsSvg() && loadingCtx_->GetSvgFillColor() != svgFillColorOpt)) {
242         LoadNotifier loadNotifier(CreateDataReadyCallback(), CreateLoadSuccessCallback(), CreateLoadFailCallback());
243 
244         bool syncLoad = imageLayoutProperty->GetSyncModeValue(false);
245         loadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(src, std::move(loadNotifier), syncLoad);
246         LOGD("start loading image %{public}s", src.ToString().c_str());
247         loadingCtx_->LoadImageData();
248     }
249     if (loadingCtx_->NeedAlt() && imageLayoutProperty->GetAlt()) {
250         auto altImageSourceInfo = imageLayoutProperty->GetAlt().value_or(ImageSourceInfo(""));
251         std::optional<Color> altSvgFillColorOpt = std::nullopt;
252         if (altImageSourceInfo.IsSvg()) {
253             altSvgFillColorOpt = imageRenderProperty->GetSvgFillColor() ? imageRenderProperty->GetSvgFillColor()
254                                                                         : altImageSourceInfo.GetFillColor();
255         }
256         LoadNotifier altLoadNotifier(CreateDataReadyCallbackForAlt(), CreateLoadSuccessCallbackForAlt(), nullptr);
257         if (!altLoadingCtx_ || altLoadingCtx_->GetSourceInfo() != altImageSourceInfo ||
258             (altLoadingCtx_ && altImageSourceInfo.IsSvg() && altSvgFillColorOpt.has_value() &&
259                 altLoadingCtx_->GetSvgFillColor() != altSvgFillColorOpt)) {
260             altLoadingCtx_ = AceType::MakeRefPtr<ImageLoadingContext>(altImageSourceInfo, std::move(altLoadNotifier));
261             altLoadingCtx_->LoadImageData();
262         }
263     }
264 }
265 
OnModifyDone()266 void ImagePattern::OnModifyDone()
267 {
268     LoadImageDataIfNeed();
269 }
270 
CreateDataReadyCallbackForAlt()271 DataReadyNotifyTask ImagePattern::CreateDataReadyCallbackForAlt()
272 {
273     auto task = [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) {
274         auto pattern = weak.Upgrade();
275         CHECK_NULL_VOID(pattern);
276         auto imageLayoutProperty = pattern->GetLayoutProperty<ImageLayoutProperty>();
277         CHECK_NULL_VOID(imageLayoutProperty);
278         auto currentAltSourceInfo = imageLayoutProperty->GetAlt().value_or(ImageSourceInfo(""));
279         if (currentAltSourceInfo != sourceInfo) {
280             LOGW("alt image sourceInfo does not match, ignore current callback. current: %{public}s vs callback's: "
281                  "%{public}s",
282                 currentAltSourceInfo.ToString().c_str(), sourceInfo.ToString().c_str());
283             return;
284         }
285         auto host = pattern->GetHost();
286         CHECK_NULL_VOID(host);
287         if (!host->IsActive()) {
288             return;
289         }
290         const auto& geometryNode = host->GetGeometryNode();
291         CHECK_NULL_VOID(geometryNode);
292         if (!geometryNode->GetContent()) {
293             host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
294             return;
295         }
296 
297         // calculate params for [altLoadingCtx] to do [MakeCanvasImage] if component size is already settled
298         pattern->altLoadingCtx_->MakeCanvasImageIfNeed(
299             geometryNode->GetContentSize(), true, imageLayoutProperty->GetImageFit().value_or(ImageFit::COVER));
300     };
301     return task;
302 }
303 
CreateLoadSuccessCallbackForAlt()304 LoadSuccessNotifyTask ImagePattern::CreateLoadSuccessCallbackForAlt()
305 {
306     auto task = [weak = WeakClaim(this)](const ImageSourceInfo& sourceInfo) {
307         auto pattern = weak.Upgrade();
308         CHECK_NULL_VOID(pattern);
309         CHECK_NULL_VOID(pattern->altLoadingCtx_);
310         auto imageLayoutProperty = pattern->GetLayoutProperty<ImageLayoutProperty>();
311         auto currentAltSourceInfo = imageLayoutProperty->GetAlt().value_or(ImageSourceInfo(""));
312         if (currentAltSourceInfo != sourceInfo) {
313             LOGW("alt image sourceInfo does not match, ignore current callback. current: %{public}s vs callback's: "
314                  "%{public}s",
315                 currentAltSourceInfo.ToString().c_str(), sourceInfo.ToString().c_str());
316             return;
317         }
318         auto host = pattern->GetHost();
319         CHECK_NULL_VOID(host);
320         host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
321         pattern->altImage_ = pattern->altLoadingCtx_->MoveCanvasImage();
322         pattern->altSrcRect_ = std::make_unique<RectF>(pattern->altLoadingCtx_->GetSrcRect());
323         pattern->altDstRect_ = std::make_unique<RectF>(pattern->altLoadingCtx_->GetDstRect());
324         pattern->SetImagePaintConfig(pattern->altImage_, *pattern->altSrcRect_, *pattern->altDstRect_,
325             pattern->altLoadingCtx_->GetSourceInfo().IsSvg());
326     };
327     return task;
328 }
329 
UpdateInternalResource(ImageSourceInfo & sourceInfo)330 void ImagePattern::UpdateInternalResource(ImageSourceInfo& sourceInfo)
331 {
332     if (!sourceInfo.IsInternalResource()) {
333         return;
334     }
335 
336     auto pipeline = PipelineBase::GetCurrentContext();
337     CHECK_NULL_VOID(pipeline);
338     auto iconTheme = pipeline->GetTheme<IconTheme>();
339     CHECK_NULL_VOID(iconTheme);
340     auto iconPath = iconTheme->GetIconPath(sourceInfo.GetResourceId());
341     if (!iconPath.empty()) {
342         sourceInfo.SetSrc(iconPath, sourceInfo.GetFillColor());
343         auto imageLayoutProperty = GetLayoutProperty<ImageLayoutProperty>();
344         CHECK_NULL_VOID(imageLayoutProperty);
345         imageLayoutProperty->UpdateImageSourceInfo(sourceInfo);
346     }
347 }
348 
OnNotifyMemoryLevel(int32_t level)349 void ImagePattern::OnNotifyMemoryLevel(int32_t level)
350 {
351     LOGI("Receive Memory level notification, level: %{public}d", level);
352     // TODO: do different data cleaning operation according to level
353     // when image component is [onShow], do not clean image data
354     // TODO: use [isActive_] to determine image data management
355     if (isShow_) {
356         return;
357     }
358     // TODO: clean cache data when cache mechanism is ready
359     // Step1: drive stateMachine to reset loading procedure
360     if (altLoadingCtx_) {
361         altLoadingCtx_->ResetLoading();
362     }
363     if (loadingCtx_) {
364         loadingCtx_->ResetLoading();
365     }
366 
367     // Step2: clean data and reset params
368     // clear src data
369     image_ = nullptr;
370     srcRect_ = RectF();
371     dstRect_ = RectF();
372     // clear alt data
373     altLoadingCtx_ = nullptr;
374     altImage_ = nullptr;
375     altDstRect_.reset();
376     altSrcRect_.reset();
377 
378     // Step3: clean rs node to release the sk_sp<SkImage> held by it
379     // TODO: release PixelMap resource when use PixelMap resource to draw image
380     auto frameNode = GetHost();
381     CHECK_NULL_VOID(frameNode);
382     auto rsRenderContext = frameNode->GetRenderContext();
383     CHECK_NULL_VOID(rsRenderContext);
384     rsRenderContext->ClearDrawCommands();
385     auto pipeline = PipelineContext::GetCurrentContext();
386     CHECK_NULL_VOID(pipeline);
387     pipeline->FlushMessages();
388 }
389 
OnWindowHide()390 void ImagePattern::OnWindowHide()
391 {
392     isShow_ = false;
393 }
394 
OnWindowShow()395 void ImagePattern::OnWindowShow()
396 {
397     isShow_ = true;
398     LoadImageDataIfNeed();
399 }
400 
OnVisibleChange(bool visible)401 void ImagePattern::OnVisibleChange(bool visible)
402 {
403     CHECK_NULL_VOID_NOLOG(image_);
404     // control svg / gif animation
405     image_->ControlAnimation(visible);
406 }
407 
OnAttachToFrameNode()408 void ImagePattern::OnAttachToFrameNode()
409 {
410     auto host = GetHost();
411     CHECK_NULL_VOID(host);
412     host->GetRenderContext()->SetClipToBounds(false);
413 
414     // register image frame node to pipeline context to receive memory level notification and window state change
415     // notification
416     auto pipeline = PipelineContext::GetCurrentContext();
417     CHECK_NULL_VOID(pipeline);
418     pipeline->AddNodesToNotifyMemoryLevel(host->GetId());
419     pipeline->AddWindowStateChangedCallback(host->GetId());
420 }
421 
OnDetachFromFrameNode(FrameNode * frameNode)422 void ImagePattern::OnDetachFromFrameNode(FrameNode* frameNode)
423 {
424     auto id = frameNode->GetId();
425     auto pipeline = AceType::DynamicCast<PipelineContext>(PipelineBase::GetCurrentContext());
426     CHECK_NULL_VOID_NOLOG(pipeline);
427     pipeline->RemoveWindowStateChangedCallback(id);
428     pipeline->RemoveNodesToNotifyMemoryLevel(id);
429     pipeline->RemoveVisibleAreaChangeNode(id);
430 }
431 
EnableDrag()432 void ImagePattern::EnableDrag()
433 {
434     auto host = GetHost();
435     CHECK_NULL_VOID(host);
436     auto size = host->GetGeometryNode()->GetContentSize();
437     auto dragStart = [imageWk = WeakClaim(RawPtr(image_)), ctxWk = WeakClaim(RawPtr(loadingCtx_)), size](
438                          const RefPtr<OHOS::Ace::DragEvent>& /*event*/,
439                          const std::string& /*extraParams*/) -> DragDropInfo {
440         DragDropInfo info;
441         auto image = imageWk.Upgrade();
442         auto ctx = ctxWk.Upgrade();
443         CHECK_NULL_RETURN(image && ctx, info);
444 
445         info.extraInfo = "image drag";
446         info.pixelMap = image->GetPixelMap();
447         if (info.pixelMap) {
448             LOGI("using pixmap onDrag");
449             return info;
450         }
451 
452         auto node = FrameNode::GetOrCreateFrameNode(V2::IMAGE_ETS_TAG, ElementRegister::GetInstance()->MakeUniqueId(),
453             []() { return AceType::MakeRefPtr<ImagePattern>(); });
454         auto pattern = node->GetPattern<ImagePattern>();
455         pattern->image_ = image;
456         pattern->loadingCtx_ = ctx;
457 
458         auto props = node->GetLayoutProperty<ImageLayoutProperty>();
459         props->UpdateImageSourceInfo(ctx->GetSourceInfo());
460         // set dragged image size to match this image
461         props->UpdateUserDefinedIdealSize(CalcSize(CalcLength(size.Width()), CalcLength(size.Height())));
462 
463         info.customNode = node;
464         return info;
465     };
466     auto eventHub = GetHost()->GetEventHub<EventHub>();
467     CHECK_NULL_VOID(eventHub);
468     eventHub->SetOnDragStart(std::move(dragStart));
469 }
470 
ToJsonValue(std::unique_ptr<JsonValue> & json) const471 void ImagePattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
472 {
473     json->Put("draggable", draggable_ ? "true" : "false");
474 }
475 
476 } // namespace OHOS::Ace::NG
477