• 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_animator/image_animator_pattern.h"
17 
18 #include "core/components_ng/pattern/image/image_pattern.h"
19 #include "core/components/image/image_theme.h"
20 #include "core/components_ng/pattern/image_animator/controlled_animator.h"
21 #include "core/pipeline_ng/pipeline_context.h"
22 
23 namespace OHOS::Ace::NG {
24 
25 namespace {
26 
27 constexpr int32_t DEFAULT_DURATION = 1000; // ms
28 constexpr uint32_t CRITICAL_TIME = 50;      // ms. If show time of image is less than this, use more cacheImages.
29 constexpr int64_t MICROSEC_TO_MILLISEC = 1000;
30 constexpr int32_t DEFAULT_ITERATIONS = 1;
31 
32 } // namespace
33 
ImageAnimatorPattern()34 ImageAnimatorPattern::ImageAnimatorPattern()
35 {
36     controlledAnimator_ = AceType::MakeRefPtr<ControlledAnimator>();
37     controlledAnimator_->SetFillMode(FillMode::FORWARDS);
38     controlledAnimator_->SetDuration(DEFAULT_DURATION);
39     ResetFormAnimationFlag();
40 }
41 
CreatePictureAnimation(int32_t size)42 std::vector<PictureInfo> ImageAnimatorPattern::CreatePictureAnimation(int32_t size)
43 {
44     auto pictureAnimation = std::vector<PictureInfo>();
45 
46     if (durationTotal_ > 0) {
47         for (int32_t index = 0; index < size; ++index) {
48             if (images_[index].duration) {
49                 pictureAnimation.emplace_back(images_[index].duration / static_cast<float>(durationTotal_), index);
50             }
51         }
52         controlledAnimator_->SetDuration(durationTotal_);
53     } else {
54         for (int32_t index = 0; index < size; ++index) {
55             pictureAnimation.emplace_back(NORMALIZED_DURATION_MAX / static_cast<float>(size), index);
56         }
57     }
58 
59     controlledAnimator_->AddListener([weak = WeakClaim(this)](int32_t index) {
60         auto imageAnimator = weak.Upgrade();
61         CHECK_NULL_VOID(imageAnimator);
62         imageAnimator->SetShowingIndex(index);
63     });
64     return pictureAnimation;
65 }
66 
SetShowingIndex(int32_t index)67 void ImageAnimatorPattern::SetShowingIndex(int32_t index)
68 {
69     auto host = GetHost();
70     CHECK_NULL_VOID(host);
71     auto imageFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildren().front());
72     CHECK_NULL_VOID(imageFrameNode);
73     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
74     CHECK_NULL_VOID(imageLayoutProperty);
75     if (index >= static_cast<int32_t>(images_.size())) {
76         LOGW("ImageAnimator update index error, index: %{public}d, size: %{public}zu", index, images_.size());
77         return;
78     }
79     nowImageIndex_ = index;
80     bool isShowingSrc = IsShowingSrc(imageFrameNode, images_[index].src);
81     auto cacheImageIter = FindCacheImageNode(images_[index].src);
82     std::string traceTag = images_[index].src;
83     if (images_[index].pixelMap != nullptr) {
84         isShowingSrc = IsShowingSrc(imageFrameNode, images_[index].pixelMap);
85         cacheImageIter = FindCacheImageNode(images_[index].pixelMap);
86         traceTag = "PixelMap";
87     }
88     if (isShowingSrc) {
89         ACE_SCOPED_TRACE("ImageAnimator same src %s, index %d", traceTag.c_str(), index);
90         UpdateShowingImageInfo(imageFrameNode, index);
91     } else if (cacheImageIter == cacheImages_.end()) {
92         ACE_SCOPED_TRACE("ImageAnimator no cache found, src %s, index %d", traceTag.c_str(), index);
93         UpdateShowingImageInfo(imageFrameNode, index);
94     } else if (cacheImageIter->isLoaded) {
95         ACE_SCOPED_TRACE("ImageAnimator useCache src %s, index %d", traceTag.c_str(), index);
96         auto cacheImageNode = cacheImageIter->imageNode;
97         host->RemoveChild(imageFrameNode);
98         host->AddChild(cacheImageNode, DEFAULT_NODE_SLOT, true);
99         host->RebuildRenderContextTree();
100         cacheImages_.erase(cacheImageIter);
101         CacheImageStruct newCacheImageStruct(imageFrameNode);
102         newCacheImageStruct.isLoaded = true;
103         cacheImages_.emplace_back(newCacheImageStruct);
104         UpdateShowingImageInfo(cacheImageNode, index);
105     } else {
106         UpdateShowingImageInfo(imageFrameNode, index);
107         // wait for cache image loading
108         ACE_SCOPED_TRACE("ImageAnimator waitForCache src %s, index %d", traceTag.c_str(), index);
109         return;
110     }
111     // update cache images
112     CHECK_NULL_VOID(cacheImages_.size());
113     int32_t nextIndex = GetNextIndex(index);
114     for (auto& cacheImage : cacheImages_) {
115         UpdateCacheImageInfo(cacheImage, nextIndex);
116         nextIndex = GetNextIndex(nextIndex);
117     }
118     host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
119 }
120 
UpdateShowingImageInfo(const RefPtr<FrameNode> & imageFrameNode,int32_t index)121 void ImageAnimatorPattern::UpdateShowingImageInfo(const RefPtr<FrameNode>& imageFrameNode, int32_t index)
122 {
123     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
124     CHECK_NULL_VOID(imageLayoutProperty);
125     if (images_[index].pixelMap == nullptr) {
126         imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(
127             images_[index].src, images_[index].bundleName, images_[index].moduleName));
128     } else {
129         imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(images_[index].pixelMap));
130     }
131     MarginProperty margin;
132     if (!fixedSize_) {
133         margin.left = CalcLength(images_[index].left);
134         margin.top = CalcLength(images_[index].top);
135         imageLayoutProperty->UpdateMargin(margin);
136         CalcSize realSize = { CalcLength(images_[index].width), CalcLength(images_[index].height) };
137         imageLayoutProperty->UpdateUserDefinedIdealSize(realSize);
138         imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_CONTENT);
139         imageFrameNode->MarkModifyDone();
140         imageFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
141         return;
142     }
143     margin.SetEdges(CalcLength(0.0));
144     imageLayoutProperty->UpdateMargin(margin);
145     imageLayoutProperty->ClearUserDefinedIdealSize(true, true);
146     imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
147     imageFrameNode->MarkModifyDone();
148     imageFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
149     ControlAnimatedImageAnimation(imageFrameNode, true);
150 }
151 
UpdateCacheImageInfo(CacheImageStruct & cacheImage,int32_t index)152 void ImageAnimatorPattern::UpdateCacheImageInfo(CacheImageStruct& cacheImage, int32_t index)
153 {
154     if (index >= static_cast<int32_t>(images_.size())) {
155         LOGW("PrepareImageInfo index error, index: %{public}d, size: %{public}zu", index, images_.size());
156         return;
157     }
158     auto imageLayoutProperty = cacheImage.imageNode->GetLayoutProperty<ImageLayoutProperty>();
159     const auto& image = images_[index];
160     if (image.pixelMap == nullptr) {
161         auto preSrc =
162             imageLayoutProperty->HasImageSourceInfo() ? imageLayoutProperty->GetImageSourceInfoValue().GetSrc() : "";
163         if (preSrc != image.src) {
164             // need to cache newImage
165             imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(image.src, image.bundleName, image.moduleName));
166             cacheImage.index = index;
167             cacheImage.isLoaded = false;
168         }
169     } else {
170         // pixelmap
171         if (imageLayoutProperty->HasImageSourceInfo()) {
172             auto preSrc = imageLayoutProperty->GetImageSourceInfoValue().GetPixmap();
173             if (preSrc != image.pixelMap) {
174                 // need to cache newImage
175                 imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(image.pixelMap));
176                 cacheImage.index = index;
177                 cacheImage.isLoaded = false;
178             }
179         } else {
180             imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(image.pixelMap));
181             cacheImage.index = index;
182             cacheImage.isLoaded = false;
183         }
184     }
185     auto host = GetHost();
186     CHECK_NULL_VOID(host);
187     auto hostSize = host->GetGeometryNode()->GetPaddingSize();
188     if (!fixedSize_) {
189         CalcSize realSize = { CalcLength(image.width), CalcLength(image.height) };
190         imageLayoutProperty->UpdateUserDefinedIdealSize(realSize);
191         cacheImage.imageNode->GetGeometryNode()->SetContentSize(
192             { image.width.ConvertToPxWithSize(hostSize.Width()), image.height.ConvertToPxWithSize(hostSize.Height()) });
193         cacheImage.imageNode->MarkModifyDone();
194         return;
195     }
196     if (!hostSize.IsPositive()) {
197         // if imageNode size is nonPositive, no pixelMap will be generated. Wait for size.
198         return;
199     }
200     imageLayoutProperty->UpdateUserDefinedIdealSize(
201         CalcSize(CalcLength(hostSize.Width()), CalcLength(hostSize.Height())));
202     cacheImage.imageNode->GetGeometryNode()->SetContentSize(hostSize);
203     cacheImage.imageNode->MarkModifyDone();
204 }
205 
FindCacheImageNode(const RefPtr<PixelMap> & src)206 std::list<ImageAnimatorPattern::CacheImageStruct>::iterator ImageAnimatorPattern::FindCacheImageNode(
207     const RefPtr<PixelMap>& src)
208 {
209     for (auto iter = cacheImages_.begin(); iter != cacheImages_.end(); ++iter) {
210         if (IsShowingSrc(iter->imageNode, src)) {
211             return iter;
212         }
213     }
214     return cacheImages_.end();
215 }
216 
FindCacheImageNode(const std::string & src)217 std::list<ImageAnimatorPattern::CacheImageStruct>::iterator ImageAnimatorPattern::FindCacheImageNode(
218     const std::string& src)
219 {
220     for (auto iter = cacheImages_.begin(); iter != cacheImages_.end(); ++iter) {
221         if (IsShowingSrc(iter->imageNode, src)) {
222             return iter;
223         }
224     }
225     return cacheImages_.end();
226 }
227 
GenerateCachedImages()228 void ImageAnimatorPattern::GenerateCachedImages()
229 {
230     CHECK_NULL_VOID(images_.size());
231     auto averageShowTime = controlledAnimator_->GetDuration() / static_cast<int32_t>(images_.size());
232     size_t cacheImageNum = static_cast<uint32_t>(averageShowTime) >= CRITICAL_TIME ? 1 : 2;
233     cacheImageNum = std::min(images_.size() - 1, cacheImageNum);
234     if (cacheImages_.size() > cacheImageNum) {
235         cacheImages_.resize(cacheImageNum);
236         return;
237     }
238     while (cacheImages_.size() < cacheImageNum) {
239         auto imageNode = FrameNode::CreateFrameNode(V2::IMAGE_ETS_TAG, -1, AceType::MakeRefPtr<ImagePattern>());
240         CHECK_NULL_VOID(imageNode);
241         auto imagePattern = AceType::DynamicCast<ImagePattern>(imageNode->GetPattern());
242         CHECK_NULL_VOID(imagePattern);
243         imagePattern->SetImageAnimator(true);
244         auto imageLayoutProperty = imageNode->GetLayoutProperty();
245         imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
246         imageLayoutProperty->UpdateAlignment(Alignment::TOP_LEFT);
247         AddImageLoadSuccessEvent(imageNode);
248         cacheImages_.emplace_back(CacheImageStruct(imageNode));
249     }
250 }
251 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & wrapper,const DirtySwapConfig & config)252 bool ImageAnimatorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& wrapper, const DirtySwapConfig& config)
253 {
254     DisablePreAnimatedImageAnimation(static_cast<uint32_t>(nowImageIndex_));
255     if (!isLayouted_) {
256         isLayouted_ = true;
257         if (fixedSize_ && images_.size()) {
258             int32_t nextIndex = GetNextIndex(nowImageIndex_);
259             for (auto& cacheImage : cacheImages_) {
260                 UpdateCacheImageInfo(cacheImage, nextIndex);
261                 nextIndex = GetNextIndex(nextIndex);
262             }
263         }
264     }
265     return false;
266 }
267 
RunAnimatorByStatus(int32_t index)268 void ImageAnimatorPattern::RunAnimatorByStatus(int32_t index)
269 {
270     switch (status_) {
271         case ControlledAnimator::ControlStatus::IDLE:
272             controlledAnimator_->Cancel();
273             ResetFormAnimationFlag();
274             SetShowingIndex(index);
275             controlledAnimator_->SetRunningIdx(index);
276             break;
277         case ControlledAnimator::ControlStatus::PAUSED:
278             controlledAnimator_->Pause();
279             ResetFormAnimationFlag();
280             break;
281         case ControlledAnimator::ControlStatus::STOPPED:
282             controlledAnimator_->Finish();
283             ResetFormAnimationFlag();
284             break;
285         default:
286             ResetFormAnimationStartTime();
287             if (isFormAnimationEnd_) {
288                 ResetFormAnimationFlag();
289                 return;
290             }
291             isReverse_ ? controlledAnimator_->Backward() : controlledAnimator_->Forward();
292     }
293 }
294 
DisablePreAnimatedImageAnimation(uint32_t index)295 void ImageAnimatorPattern::DisablePreAnimatedImageAnimation(uint32_t index)
296 {
297     if (index >= static_cast<uint32_t>(images_.size())) {
298         TAG_LOGW(AceLogTag::ACE_IMAGE, "ImageAnimator get index error, index: %{public}d, size: %{public}zu", index,
299             images_.size());
300         return;
301     }
302     auto cacheImageIter = FindCacheImageNode(images_[index].src);
303     if (images_[index].pixelMap != nullptr) {
304         cacheImageIter = FindCacheImageNode(images_[index].pixelMap);
305     }
306     if (cacheImageIter != cacheImages_.end()) {
307         auto cacheImageNode = cacheImageIter->imageNode;
308         ControlAnimatedImageAnimation(cacheImageNode, false);
309     }
310 }
311 
OnModifyDone()312 void ImageAnimatorPattern::OnModifyDone()
313 {
314     auto host = GetHost();
315     CHECK_NULL_VOID(host);
316     Pattern::OnModifyDone();
317     UpdateBorderRadius();
318     auto size = static_cast<int32_t>(images_.size());
319     if (size <= 0) {
320         LOGE("image size is less than 0.");
321         return;
322     }
323     GenerateCachedImages();
324     auto index = nowImageIndex_;
325     if ((status_ == ControlledAnimator::ControlStatus::IDLE || status_ == ControlledAnimator::ControlStatus::STOPPED) &&
326         !firstUpdateEvent_) {
327         index = isReverse_ ? (size - 1) : 0;
328     }
329 
330     if (imagesChangedFlag_) {
331         controlledAnimator_->ClearInterpolators();
332         controlledAnimator_->AddInterpolator(CreatePictureAnimation(size));
333         AdaptSelfSize();
334         imagesChangedFlag_ = false;
335     } else if (isImagesSame_) {
336         AdaptSelfSize();
337         isImagesSame_ = false;
338     }
339     controlledAnimator_->SetIteration(GetIteration());
340     if (firstUpdateEvent_) {
341         UpdateEventCallback();
342         firstUpdateEvent_ = false;
343         auto imageFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildren().front());
344         AddImageLoadSuccessEvent(imageFrameNode);
345     }
346     UpdateFormDurationByRemainder();
347     RunAnimatorByStatus(index);
348 }
349 
UpdateBorderRadius()350 void ImageAnimatorPattern::UpdateBorderRadius()
351 {
352     auto host = GetHost();
353     auto context = host->GetContextRefPtr();
354     CHECK_NULL_VOID(context);
355     auto imageTheme = context->GetTheme<ImageTheme>();
356     CHECK_NULL_VOID(imageTheme);
357     auto renderContext = host->GetRenderContext();
358     CHECK_NULL_VOID(renderContext);
359     if (!renderContext->HasBorderRadius() && imageTheme->GetCornerRadius() > 0.0_vp) {
360         renderContext->UpdateBorderRadius(BorderRadiusProperty(imageTheme->GetCornerRadius()));
361     }
362     if (!renderContext->HasClipEdge() && imageTheme->GetClipEdge()) {
363         renderContext->UpdateClipEdge(imageTheme->GetClipEdge());
364     }
365 }
366 
RegisterVisibleAreaChange()367 void ImageAnimatorPattern::RegisterVisibleAreaChange()
368 {
369     auto pipeline = GetContext();
370     // register to onVisibleAreaChange
371     CHECK_NULL_VOID(pipeline);
372     auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
373         auto self = weak.Upgrade();
374         CHECK_NULL_VOID(self);
375         if (self->CheckIfNeedVisibleAreaChange()) {
376             self->OnVisibleAreaChange(visible, ratio);
377         }
378     };
379     auto host = GetHost();
380     CHECK_NULL_VOID(host);
381     // add visibleAreaChangeNode(inner callback)
382     std::vector<double> ratioList = {0.0};
383     pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
384 }
385 
OnVisibleAreaChange(bool visible,double ratio)386 void ImageAnimatorPattern::OnVisibleAreaChange(bool visible, double ratio)
387 {
388     ACE_SCOPED_TRACE("ImageAnimator OnVisibleAreaChange visible: [%d]", visible);
389     if (SystemProperties::GetDebugEnabled()) {
390         TAG_LOGI(AceLogTag::ACE_IMAGE, "ImageAnimator OnVisibleAreaChange visible:%{public}d", visible);
391     }
392     if (!visible) {
393         OnInActive();
394     } else {
395         OnActive();
396     }
397 }
398 
OnAttachToFrameNode()399 void ImageAnimatorPattern::OnAttachToFrameNode()
400 {
401     auto host = GetHost();
402     CHECK_NULL_VOID(host);
403     auto renderContext = host->GetRenderContext();
404     CHECK_NULL_VOID(renderContext);
405     renderContext->SetClipToFrame(true);
406 
407     UpdateBorderRadius();
408     RegisterVisibleAreaChange();
409 }
410 
UpdateEventCallback()411 void ImageAnimatorPattern::UpdateEventCallback()
412 {
413     auto eventHub = GetEventHub<ImageAnimatorEventHub>();
414     CHECK_NULL_VOID(eventHub);
415 
416     controlledAnimator_->ClearAllListeners();
417     auto startEvent = eventHub->GetStartEvent();
418     if (startEvent != nullptr) {
419         controlledAnimator_->AddStartListener([startEvent] { startEvent(); });
420     }
421 
422     auto stopEvent = eventHub->GetStopEvent();
423     if (stopEvent != nullptr) {
424         controlledAnimator_->AddStopListener([stopEvent] { stopEvent(); });
425     }
426 
427     auto pauseEvent = eventHub->GetPauseEvent();
428     if (pauseEvent != nullptr) {
429         controlledAnimator_->AddPauseListener([pauseEvent] { pauseEvent(); });
430     }
431 
432     auto repeatEvent = eventHub->GetRepeatEvent();
433     if (repeatEvent != nullptr) {
434         controlledAnimator_->AddRepeatListener([repeatEvent] { repeatEvent(); });
435     }
436 
437     auto cancelEvent = eventHub->GetCancelEvent();
438     if (cancelEvent != nullptr) {
439         controlledAnimator_->AddCancelListener([cancelEvent] { cancelEvent(); });
440     }
441 }
442 
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const443 void ImageAnimatorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
444 {
445     Pattern::ToJsonValue(json, filter);
446     /* no fixed attr below, just return */
447     if (filter.IsFastFilter()) {
448         return;
449     }
450     static const char* STATUS_MODE[] = { "AnimationStatus.Initial", "AnimationStatus.Running", "AnimationStatus.Paused",
451         "AnimationStatus.Stopped" };
452     json->PutExtAttr("state", STATUS_MODE[static_cast<int32_t>(status_)], filter);
453     json->PutExtAttr("duration", std::to_string(controlledAnimator_->GetDuration()).c_str(), filter);
454     json->PutExtAttr("reverse", isReverse_ ? "true" : "false", filter);
455     json->PutExtAttr("fixedSize", fixedSize_ ? "true" : "false", filter);
456     static const char* FILL_MODE[] = { "FillMode.None", "FillMode.Forwards", "FillMode.Backwards", "FillMode.Both" };
457     json->PutExtAttr("fillMode", FILL_MODE[static_cast<int32_t>(controlledAnimator_->GetFillMode())], filter);
458     json->PutExtAttr("iterations", std::to_string(controlledAnimator_->GetIteration()).c_str(), filter);
459     json->PutExtAttr("images", ImagesToString().c_str(), filter);
460     json->PutExtAttr("monitorInvisibleArea", isAutoMonitorInvisibleArea_ ? "true" : "false", filter);
461 }
462 
ImagesToString() const463 std::string ImageAnimatorPattern::ImagesToString() const
464 {
465     auto imageArray = JsonUtil::CreateArray(true);
466     for (const auto& image : images_) {
467         auto item = JsonUtil::Create(true);
468         item->Put("src", image.src.c_str());
469         item->Put("left", image.left.ToString().c_str());
470         item->Put("top", image.top.ToString().c_str());
471         item->Put("width", image.width.ToString().c_str());
472         item->Put("height", image.height.ToString().c_str());
473         item->Put("duration", std::to_string(image.duration).c_str());
474         imageArray->Put(item);
475     }
476     return imageArray->ToString();
477 }
478 
AdaptSelfSize()479 void ImageAnimatorPattern::AdaptSelfSize()
480 {
481     auto host = GetHost();
482     CHECK_NULL_VOID(host);
483     const auto& layoutProperty = host->GetLayoutProperty();
484     CHECK_NULL_VOID(layoutProperty);
485     if (layoutProperty->GetCalcLayoutConstraint() && layoutProperty->GetCalcLayoutConstraint()->selfIdealSize &&
486         layoutProperty->GetCalcLayoutConstraint()->selfIdealSize->IsValid()) {
487         return;
488     }
489     Dimension maxWidth;
490     Dimension maxHeight;
491     double maxWidthPx = 0.0;
492     double maxHeightPx = 0.0;
493     for (const auto& image : images_) {
494         if (image.width.Unit() != DimensionUnit::PERCENT) {
495             auto widthPx = image.width.ConvertToPx();
496             if (widthPx > maxWidthPx) {
497                 maxWidthPx = widthPx;
498                 maxWidth = image.width;
499             }
500         }
501         if (image.height.Unit() != DimensionUnit::PERCENT) {
502             auto heightPx = image.height.ConvertToPx();
503             if (heightPx > maxHeightPx) {
504                 maxHeightPx = heightPx;
505                 maxHeight = image.height;
506             }
507         }
508     }
509     if (!maxWidth.IsValid() || !maxHeight.IsValid()) {
510         return;
511     }
512     const auto& layoutConstraint = layoutProperty->GetCalcLayoutConstraint();
513     if (!layoutConstraint || !layoutConstraint->selfIdealSize) {
514         layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(maxWidth), CalcLength(maxHeight)));
515         return;
516     }
517     if (!layoutConstraint->selfIdealSize->Width()) {
518         layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(maxWidth), std::nullopt));
519         return;
520     }
521     layoutProperty->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(maxHeight)));
522 }
523 
GetNextIndex(int32_t preIndex)524 int32_t ImageAnimatorPattern::GetNextIndex(int32_t preIndex)
525 {
526     if (isReverse_) {
527         return preIndex == 0 ? (static_cast<int32_t>(images_.size()) - 1) : (preIndex - 1);
528     }
529     return (preIndex + 1) % static_cast<int32_t>(images_.size());
530 }
531 
AddImageLoadSuccessEvent(const RefPtr<FrameNode> & imageFrameNode)532 void ImageAnimatorPattern::AddImageLoadSuccessEvent(const RefPtr<FrameNode>& imageFrameNode)
533 {
534     CHECK_NULL_VOID(imageFrameNode);
535     auto eventHub = imageFrameNode->GetEventHub<ImageEventHub>();
536     eventHub->SetOnComplete(
537         [weakImage = WeakPtr<FrameNode>(imageFrameNode), weak = WeakClaim(this)](const LoadImageSuccessEvent& info) {
538             if (info.GetLoadingStatus() != 1) {
539                 // status 1 means load success. Only need loadSuccess event.
540                 return;
541             }
542             auto pattern = weak.Upgrade();
543             CHECK_NULL_VOID(pattern);
544             auto cacheImageNode = weakImage.Upgrade();
545             CHECK_NULL_VOID(cacheImageNode);
546             auto imageAnimator = pattern->GetHost();
547             CHECK_NULL_VOID(imageAnimator);
548             auto cacheLayoutProperty = cacheImageNode->GetLayoutProperty<ImageLayoutProperty>();
549             auto cacheSrc = cacheLayoutProperty->GetImageSourceInfoValue(ImageSourceInfo()).GetSrc();
550             ACE_SCOPED_TRACE("ImageAnimator cache succeed. src %s", cacheSrc.c_str());
551             auto iter = std::find_if(pattern->cacheImages_.begin(), pattern->cacheImages_.end(),
552                 [&cacheImageNode](const CacheImageStruct& other) { return other.imageNode == cacheImageNode; });
553             if (iter == pattern->cacheImages_.end()) {
554                 return;
555             }
556             iter->isLoaded = true;
557             if (pattern->nowImageIndex_ >= static_cast<int32_t>(pattern->images_.size())) {
558                 LOGW("ImageAnimator showImage index is invalid");
559                 return;
560             }
561             if (pattern->images_[pattern->nowImageIndex_].pixelMap == nullptr) {
562                 if (pattern->nowImageIndex_ == iter->index &&
563                     IsShowingSrc(cacheImageNode, pattern->images_[pattern->nowImageIndex_].src)) {
564                     pattern->SetShowingIndex(pattern->nowImageIndex_);
565                 } else if (pattern->nowImageIndex_ == 0) {
566                     pattern->EnableFirstAnimatedImageAnimation();
567                 }
568             } else {
569                 if (pattern->nowImageIndex_ == iter->index &&
570                     IsShowingSrc(cacheImageNode, pattern->images_[pattern->nowImageIndex_].pixelMap)) {
571                     pattern->SetShowingIndex(pattern->nowImageIndex_);
572                 }
573             }
574         });
575 }
576 
IsShowingSrc(const RefPtr<FrameNode> & imageFrameNode,const std::string & src)577 bool ImageAnimatorPattern::IsShowingSrc(const RefPtr<FrameNode>& imageFrameNode, const std::string& src)
578 {
579     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
580     return imageLayoutProperty->HasImageSourceInfo() && imageLayoutProperty->GetImageSourceInfoValue().GetSrc() == src;
581 }
582 
IsShowingSrc(const RefPtr<FrameNode> & imageFrameNode,const RefPtr<PixelMap> & src)583 bool ImageAnimatorPattern::IsShowingSrc(const RefPtr<FrameNode>& imageFrameNode, const RefPtr<PixelMap>& src)
584 {
585     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
586     return imageLayoutProperty->HasImageSourceInfo()
587         && imageLayoutProperty->GetImageSourceInfoValue().GetPixmap() == src;
588 }
589 
IsFormRender()590 bool ImageAnimatorPattern::IsFormRender()
591 {
592     auto pipeline = PipelineBase::GetCurrentContext();
593     CHECK_NULL_RETURN(pipeline, false);
594 
595     auto container = Container::Current();
596     bool isDynamicComponent = container && container->IsDynamicRender() &&
597                               container->GetUIContentType() == UIContentType::DYNAMIC_COMPONENT;
598     return pipeline->IsFormRender() && !isDynamicComponent;
599 }
600 
UpdateFormDurationByRemainder()601 void ImageAnimatorPattern::UpdateFormDurationByRemainder()
602 {
603     if (IsFormRender()) {
604         if (!isFormAnimationStart_) {
605             formAnimationRemainder_ =
606                 DEFAULT_DURATION - (GetMicroTickCount() - formAnimationStartTime_) / MICROSEC_TO_MILLISEC;
607         }
608         if ((formAnimationRemainder_ > 0) && (controlledAnimator_->GetDuration() > formAnimationRemainder_)) {
609             controlledAnimator_->SetDuration(formAnimationRemainder_);
610         }
611         if (formAnimationRemainder_ <= 0) {
612             isFormAnimationEnd_ = true;
613         }
614     }
615 }
616 
ResetFormAnimationStartTime()617 void ImageAnimatorPattern::ResetFormAnimationStartTime()
618 {
619     if (isFormAnimationStart_) {
620         isFormAnimationStart_ = false;
621         formAnimationStartTime_ = GetMicroTickCount();
622     }
623 }
624 
ResetFormAnimationFlag()625 void ImageAnimatorPattern::ResetFormAnimationFlag()
626 {
627     if (IsFormRender()) {
628         formAnimationRemainder_ = DEFAULT_DURATION;
629         isFormAnimationStart_ = true;
630         isFormAnimationEnd_ = false;
631     }
632 }
633 
SetIteration(int32_t iteration)634 void ImageAnimatorPattern::SetIteration(int32_t iteration)
635 {
636     if (IsFormRender()) {
637         iteration = DEFAULT_ITERATIONS;
638     }
639     iteration_ = iteration;
640 }
641 
SetDuration(int32_t duration)642 void ImageAnimatorPattern::SetDuration(int32_t duration)
643 {
644     int32_t finalDuration = durationTotal_ > 0 ? durationTotal_ : duration;
645     if (IsFormRender()) {
646         finalDuration = finalDuration < DEFAULT_DURATION ? finalDuration : DEFAULT_DURATION;
647     }
648     if (controlledAnimator_->GetDuration() == finalDuration) {
649         controlledAnimator_->RemoveInnerRepeatListener();
650         return;
651     }
652     if (controlledAnimator_->GetControlStatus() == ControlledAnimator::ControlStatus::IDLE ||
653         controlledAnimator_->GetControlStatus() == ControlledAnimator::ControlStatus::STOPPED) {
654         controlledAnimator_->SetDuration(finalDuration);
655         controlledAnimator_->RemoveInnerRepeatListener();
656         return;
657     }
658     // if animator is running or paused, duration will work next time
659     controlledAnimator_->RemoveInnerRepeatListener();
660     controlledAnimator_->AddInnerRepeatListener([weak = WeakClaim(this), finalDuration]() {
661         auto imageAnimator = weak.Upgrade();
662         CHECK_NULL_VOID(imageAnimator);
663         imageAnimator->controlledAnimator_->SetDuration(finalDuration);
664     });
665 }
EnableFirstAnimatedImageAnimation()666 void ImageAnimatorPattern::EnableFirstAnimatedImageAnimation()
667 {
668     auto host = GetHost();
669     CHECK_NULL_VOID(host);
670     auto children = host->GetChildren();
671     if (children.empty()) {
672         return;
673     }
674     auto imageFrameNode = AceType::DynamicCast<FrameNode>(children.front());
675     ControlAnimatedImageAnimation(imageFrameNode, true);
676 }
ControlAnimatedImageAnimation(const RefPtr<FrameNode> & imageFrameNode,bool play)677 void ImageAnimatorPattern::ControlAnimatedImageAnimation(const RefPtr<FrameNode>& imageFrameNode, bool play)
678 {
679     CHECK_NULL_VOID(imageFrameNode);
680     auto imagePattern = AceType::DynamicCast<ImagePattern>(imageFrameNode->GetPattern());
681     CHECK_NULL_VOID(imagePattern);
682     auto image = imagePattern->GetCanvasImage();
683     CHECK_NULL_VOID(image);
684     if (!image->IsStatic()) {
685         image->ControlAnimation(play);
686     }
687 }
688 } // namespace OHOS::Ace::NG
689