• 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 #include <algorithm>
18 #include <string>
19 
20 #include "base/log/ace_trace.h"
21 #include "core/components_ng/pattern/image/image_event_hub.h"
22 #include "core/components_ng/pattern/image/image_layout_property.h"
23 #include "core/components_ng/pattern/image/image_pattern.h"
24 #include "core/components_ng/property/calc_length.h"
25 #include "core/components_ng/property/property.h"
26 #include "core/pipeline_ng/pipeline_context.h"
27 
28 namespace OHOS::Ace::NG {
29 
30 namespace {
31 
32 constexpr uint32_t DEFAULT_DURATION = 1000; // ms
33 constexpr uint32_t CRITICAL_TIME = 50;      // ms. If show time of image is less than this, use more cacheImages.
34 
35 } // namespace
36 
ImageAnimatorPattern()37 ImageAnimatorPattern::ImageAnimatorPattern()
38 {
39     animator_ = CREATE_ANIMATOR(PipelineContext::GetCurrentContext());
40     animator_->SetFillMode(FillMode::FORWARDS);
41     animator_->SetDuration(DEFAULT_DURATION);
42 }
43 
CreatePictureAnimation(int32_t size)44 RefPtr<PictureAnimation<int32_t>> ImageAnimatorPattern::CreatePictureAnimation(int32_t size)
45 {
46     auto pictureAnimation = MakeRefPtr<PictureAnimation<int32_t>>();
47 
48     if (durationTotal_ > 0) {
49         for (int32_t index = 0; index < size; ++index) {
50             pictureAnimation->AddPicture(images_[index].duration / static_cast<float>(durationTotal_), index);
51         }
52         animator_->SetDuration(durationTotal_);
53     } else {
54         for (int32_t index = 0; index < size; ++index) {
55             pictureAnimation->AddPicture(NORMALIZED_DURATION_MAX / static_cast<float>(size), index);
56         }
57     }
58 
59     pictureAnimation->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     auto cacheImageIter = FindCacheImageNode(images_[index].src);
81     if (IsShowingSrc(imageFrameNode, images_[index].src)) {
82         ACE_SCOPED_TRACE("ImageAnimator same src %s, index %d", images_[index].src.c_str(), index);
83         UpdateShowingImageInfo(imageFrameNode, index);
84     } else if (cacheImageIter == cacheImages_.end()) {
85         ACE_SCOPED_TRACE("ImageAnimator no cache found, src %s, index %d", images_[index].src.c_str(), index);
86         UpdateShowingImageInfo(imageFrameNode, index);
87     } else if (cacheImageIter->isLoaded) {
88         ACE_SCOPED_TRACE("ImageAnimator useCache src %s, index %d", images_[index].src.c_str(), index);
89         auto cacheImageNode = cacheImageIter->imageNode;
90         host->RemoveChild(imageFrameNode);
91         host->AddChild(cacheImageNode, DEFAULT_NODE_SLOT, true);
92         host->RebuildRenderContextTree();
93         cacheImages_.erase(cacheImageIter);
94         cacheImages_.emplace_back(CacheImageStruct(imageFrameNode));
95         UpdateShowingImageInfo(cacheImageNode, index);
96     } else {
97         // wait for cache image loading
98         ACE_SCOPED_TRACE("ImageAnimator waitForCache src %s, index %d", images_[index].src.c_str(), index);
99         return;
100     }
101     // update cache images
102     CHECK_NULL_VOID_NOLOG(cacheImages_.size());
103     int32_t nextIndex = GetNextIndex(index);
104     for (auto& cacheImage : cacheImages_) {
105         UpdateCacheImageInfo(cacheImage, nextIndex);
106         nextIndex = GetNextIndex(nextIndex);
107     }
108 }
109 
UpdateShowingImageInfo(const RefPtr<FrameNode> & imageFrameNode,int32_t index)110 void ImageAnimatorPattern::UpdateShowingImageInfo(const RefPtr<FrameNode>& imageFrameNode, int32_t index)
111 {
112     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
113     CHECK_NULL_VOID(imageLayoutProperty);
114     imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(images_[index].src));
115     MarginProperty margin;
116     if (!fixedSize_) {
117         margin.left = CalcLength(images_[index].left);
118         margin.top = CalcLength(images_[index].top);
119         imageLayoutProperty->UpdateMargin(margin);
120         CalcSize realSize = { CalcLength(images_[index].width), CalcLength(images_[index].height) };
121         imageLayoutProperty->UpdateUserDefinedIdealSize(realSize);
122         imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_CONTENT);
123         imageFrameNode->MarkModifyDone();
124         imageFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
125         return;
126     }
127     margin.SetEdges(CalcLength(0.0));
128     imageLayoutProperty->UpdateMargin(margin);
129     imageLayoutProperty->ClearUserDefinedIdealSize(true, true);
130     imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
131     imageFrameNode->MarkModifyDone();
132     imageFrameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
133 }
134 
UpdateCacheImageInfo(CacheImageStruct & cacheImage,int32_t index)135 void ImageAnimatorPattern::UpdateCacheImageInfo(CacheImageStruct& cacheImage, int32_t index)
136 {
137     if (index >= static_cast<int32_t>(images_.size())) {
138         LOGW("PrepareImageInfo index error, index: %{public}d, size: %{public}zu", index, images_.size());
139         return;
140     }
141     auto imageLayoutProperty = cacheImage.imageNode->GetLayoutProperty<ImageLayoutProperty>();
142     auto preSrc =
143         imageLayoutProperty->HasImageSourceInfo() ? imageLayoutProperty->GetImageSourceInfoValue().GetSrc() : "";
144     if (preSrc != images_[index].src) {
145         // need to cache newImage
146         imageLayoutProperty->UpdateImageSourceInfo(ImageSourceInfo(images_[index].src));
147         cacheImage.index = index;
148         cacheImage.isLoaded = false;
149     }
150     if (!fixedSize_) {
151         CalcSize realSize = { CalcLength(images_[index].width), CalcLength(images_[index].height) };
152         imageLayoutProperty->UpdateUserDefinedIdealSize(realSize);
153         cacheImage.imageNode->MarkModifyDone();
154         return;
155     }
156     auto host = GetHost();
157     CHECK_NULL_VOID(host);
158     auto hostSize = host->GetGeometryNode()->GetPaddingSize();
159     if (!hostSize.IsPositive()) {
160         // if imageNode size is nonPositive, no pixelMap will be generated. Wait for size.
161         return;
162     }
163     imageLayoutProperty->UpdateUserDefinedIdealSize(
164         CalcSize(CalcLength(hostSize.Width()), CalcLength(hostSize.Height())));
165     cacheImage.imageNode->MarkModifyDone();
166 }
167 
FindCacheImageNode(const std::string & src)168 std::list<ImageAnimatorPattern::CacheImageStruct>::iterator ImageAnimatorPattern::FindCacheImageNode(
169     const std::string& src)
170 {
171     for (auto iter = cacheImages_.begin(); iter != cacheImages_.end(); ++iter) {
172         if (IsShowingSrc(iter->imageNode, src)) {
173             return iter;
174         }
175     }
176     return cacheImages_.end();
177 }
178 
GenerateCachedImages()179 void ImageAnimatorPattern::GenerateCachedImages()
180 {
181     CHECK_NULL_VOID(images_.size());
182     auto averageShowTime = animator_->GetDuration() / images_.size();
183     size_t cacheImageNum = averageShowTime >= CRITICAL_TIME ? 1 : 2;
184     cacheImageNum = std::min(images_.size() - 1, cacheImageNum);
185     if (cacheImages_.size() > cacheImageNum) {
186         cacheImages_.resize(cacheImageNum);
187         return;
188     }
189     while (cacheImages_.size() < cacheImageNum) {
190         auto imageNode = FrameNode::CreateFrameNode(V2::IMAGE_ETS_TAG, -1, AceType::MakeRefPtr<ImagePattern>());
191         auto imageLayoutProperty = imageNode->GetLayoutProperty();
192         imageLayoutProperty->UpdateMeasureType(MeasureType::MATCH_PARENT);
193         imageLayoutProperty->UpdateAlignment(Alignment::TOP_LEFT);
194         AddImageLoadSuccessEvent(imageNode);
195         cacheImages_.emplace_back(CacheImageStruct(imageNode));
196     }
197 }
198 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & wrapper,const DirtySwapConfig & config)199 bool ImageAnimatorPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& wrapper, const DirtySwapConfig& config)
200 {
201     if (!isLayouted_) {
202         isLayouted_ = true;
203         if (fixedSize_ && images_.size()) {
204             int32_t nextIndex = GetNextIndex(nowImageIndex_);
205             for (auto& cacheImage : cacheImages_) {
206                 UpdateCacheImageInfo(cacheImage, nextIndex);
207                 nextIndex = GetNextIndex(nextIndex);
208             }
209         }
210     }
211     return false;
212 }
213 
OnModifyDone()214 void ImageAnimatorPattern::OnModifyDone()
215 {
216     auto host = GetHost();
217     CHECK_NULL_VOID(host);
218     Pattern::OnModifyDone();
219     auto size = static_cast<int32_t>(images_.size());
220     if (size <= 0) {
221         LOGE("image size is less than 0.");
222         return;
223     }
224     GenerateCachedImages();
225     auto index = nowImageIndex_;
226     if ((status_ == Animator::Status::IDLE || status_ == Animator::Status::STOPPED) && firstUpdateEvent_) {
227         index = isReverse_ ? (size - 1) : 0;
228     }
229     SetShowingIndex(index);
230 
231     if (imagesChangedFlag_) {
232         animator_->ClearInterpolators();
233         animator_->AddInterpolator(CreatePictureAnimation(size));
234         AdaptSelfSize();
235         imagesChangedFlag_ = false;
236     }
237     if (firstUpdateEvent_) {
238         UpdateEventCallback();
239         firstUpdateEvent_ = false;
240         auto imageFrameNode = AceType::DynamicCast<FrameNode>(host->GetChildren().front());
241         AddImageLoadSuccessEvent(imageFrameNode);
242     }
243 
244     switch (status_) {
245         case Animator::Status::IDLE:
246             animator_->Cancel();
247             break;
248         case Animator::Status::PAUSED:
249             animator_->Pause();
250             break;
251         case Animator::Status::STOPPED:
252             animator_->Finish();
253             break;
254         default:
255             isReverse_ ? animator_->Backward() : animator_->Forward();
256     }
257 }
258 
OnAttachToFrameNode()259 void ImageAnimatorPattern::OnAttachToFrameNode()
260 {
261     auto host = GetHost();
262     CHECK_NULL_VOID(host);
263     auto context = host->GetRenderContext();
264     CHECK_NULL_VOID(context);
265     context->SetClipToFrame(true);
266 }
267 
UpdateEventCallback()268 void ImageAnimatorPattern::UpdateEventCallback()
269 {
270     auto eventHub = GetEventHub<ImageAnimatorEventHub>();
271     CHECK_NULL_VOID(eventHub);
272 
273     animator_->ClearAllListeners();
274     auto startEvent = eventHub->GetStartEvent();
275     if (startEvent != nullptr) {
276         animator_->AddStartListener([startEvent] { startEvent(); });
277     }
278 
279     auto stopEvent = eventHub->GetStopEvent();
280     if (stopEvent != nullptr) {
281         animator_->AddStopListener([stopEvent] { stopEvent(); });
282     }
283 
284     auto pauseEvent = eventHub->GetPauseEvent();
285     if (pauseEvent != nullptr) {
286         animator_->AddPauseListener([pauseEvent] { pauseEvent(); });
287     }
288 
289     auto repeatEvent = eventHub->GetRepeatEvent();
290     if (repeatEvent != nullptr) {
291         animator_->AddRepeatListener([repeatEvent] { repeatEvent(); });
292     }
293 
294     auto cancelEvent = eventHub->GetCancelEvent();
295     if (cancelEvent != nullptr) {
296         animator_->AddIdleListener([cancelEvent] { cancelEvent(); });
297     }
298 }
299 
ToJsonValue(std::unique_ptr<JsonValue> & json) const300 void ImageAnimatorPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
301 {
302     Pattern::ToJsonValue(json);
303     static const char* STATUS_MODE[] = { "AnimationStatus.Initial", "AnimationStatus.Running", "AnimationStatus.Paused",
304         "AnimationStatus.Stopped" };
305     json->Put("state", STATUS_MODE[static_cast<int32_t>(status_)]);
306     json->Put("duration", std::to_string(animator_->GetDuration()).c_str());
307     json->Put("reverse", isReverse_ ? "true" : "false");
308     json->Put("fixedSize", fixedSize_ ? "true" : "false");
309     static const char* FILL_MODE[] = { "FillMode.None", "FillMode.Forwards", "FillMode.Backwards", "FillMode.Both" };
310     json->Put("fillMode", FILL_MODE[static_cast<int32_t>(animator_->GetFillMode())]);
311     json->Put("iterations", std::to_string(animator_->GetIteration()).c_str());
312     json->Put("images", ImagesToString().c_str());
313 }
314 
ImagesToString() const315 std::string ImageAnimatorPattern::ImagesToString() const
316 {
317     auto imageArray = JsonUtil::CreateArray(true);
318     for (const auto& image : images_) {
319         auto item = JsonUtil::Create(true);
320         item->Put("src", image.src.c_str());
321         item->Put("left", image.left.ToString().c_str());
322         item->Put("top", image.top.ToString().c_str());
323         item->Put("width", image.width.ToString().c_str());
324         item->Put("height", image.height.ToString().c_str());
325         item->Put("duration", std::to_string(image.duration).c_str());
326         imageArray->Put(item);
327     }
328     return imageArray->ToString();
329 }
330 
AdaptSelfSize()331 void ImageAnimatorPattern::AdaptSelfSize()
332 {
333     auto host = GetHost();
334     CHECK_NULL_VOID(host);
335     const auto& layoutProperty = host->GetLayoutProperty();
336     CHECK_NULL_VOID(layoutProperty);
337     if (layoutProperty->GetCalcLayoutConstraint() && layoutProperty->GetCalcLayoutConstraint()->selfIdealSize &&
338         layoutProperty->GetCalcLayoutConstraint()->selfIdealSize->IsValid()) {
339         return;
340     }
341     Dimension maxWidth;
342     Dimension maxHeight;
343     double maxWidthPx = 0.0;
344     double maxHeightPx = 0.0;
345     for (const auto& image : images_) {
346         if (image.width.Unit() != DimensionUnit::PERCENT) {
347             auto widthPx = image.width.ConvertToPx();
348             if (widthPx > maxWidthPx) {
349                 maxWidthPx = widthPx;
350                 maxWidth = image.width;
351             }
352         }
353         if (image.height.Unit() != DimensionUnit::PERCENT) {
354             auto heightPx = image.height.ConvertToPx();
355             if (heightPx > maxHeightPx) {
356                 maxHeightPx = heightPx;
357                 maxHeight = image.height;
358             }
359         }
360     }
361     if (!maxWidth.IsValid() || !maxHeight.IsValid()) {
362         return;
363     }
364     const auto& layoutConstraint = layoutProperty->GetCalcLayoutConstraint();
365     if (!layoutConstraint || !layoutConstraint->selfIdealSize) {
366         layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(maxWidth), CalcLength(maxHeight)));
367         return;
368     }
369     if (!layoutConstraint->selfIdealSize->Width()) {
370         layoutProperty->UpdateUserDefinedIdealSize(CalcSize(CalcLength(maxWidth), std::nullopt));
371         return;
372     }
373     layoutProperty->UpdateUserDefinedIdealSize(CalcSize(std::nullopt, CalcLength(maxHeight)));
374 }
375 
GetNextIndex(int32_t preIndex)376 int32_t ImageAnimatorPattern::GetNextIndex(int32_t preIndex)
377 {
378     if (isReverse_) {
379         return preIndex == 0 ? (static_cast<int32_t>(images_.size()) - 1) : (preIndex - 1);
380     }
381     return (preIndex + 1) % static_cast<int32_t>(images_.size());
382 }
383 
AddImageLoadSuccessEvent(const RefPtr<FrameNode> & imageFrameNode)384 void ImageAnimatorPattern::AddImageLoadSuccessEvent(const RefPtr<FrameNode>& imageFrameNode)
385 {
386     CHECK_NULL_VOID(imageFrameNode);
387     auto eventHub = imageFrameNode->GetEventHub<ImageEventHub>();
388     eventHub->SetOnComplete(
389         [weakImage = WeakPtr<FrameNode>(imageFrameNode), weak = WeakClaim(this)](const LoadImageSuccessEvent& info) {
390             if (info.GetLoadingStatus() != 1) {
391                 // status 1 means load success. Only need loadSuccess event.
392                 return;
393             }
394             auto pattern = weak.Upgrade();
395             CHECK_NULL_VOID(pattern);
396             auto cacheImageNode = weakImage.Upgrade();
397             CHECK_NULL_VOID(cacheImageNode);
398             auto imageAnimator = pattern->GetHost();
399             CHECK_NULL_VOID(imageAnimator);
400             auto cacheLayoutProperty = cacheImageNode->GetLayoutProperty<ImageLayoutProperty>();
401             auto cacheSrc = cacheLayoutProperty->GetImageSourceInfoValue(ImageSourceInfo()).GetSrc();
402             ACE_SCOPED_TRACE("ImageAnimator cache succeed. src %s", cacheSrc.c_str());
403             auto iter = std::find_if(pattern->cacheImages_.begin(), pattern->cacheImages_.end(),
404                 [&cacheImageNode](const CacheImageStruct& other) { return other.imageNode == cacheImageNode; });
405             if (iter == pattern->cacheImages_.end()) {
406                 return;
407             }
408             iter->isLoaded = true;
409             if (pattern->nowImageIndex_ >= static_cast<int32_t>(pattern->images_.size())) {
410                 LOGW("ImageAnimator showImage index is invalid");
411                 return;
412             }
413             if (pattern->nowImageIndex_ == iter->index &&
414                 IsShowingSrc(cacheImageNode, pattern->images_[pattern->nowImageIndex_].src)) {
415                 pattern->SetShowingIndex(pattern->nowImageIndex_);
416             }
417         });
418 }
419 
IsShowingSrc(const RefPtr<FrameNode> & imageFrameNode,const std::string & src)420 bool ImageAnimatorPattern::IsShowingSrc(const RefPtr<FrameNode>& imageFrameNode, const std::string& src)
421 {
422     auto imageLayoutProperty = imageFrameNode->GetLayoutProperty<ImageLayoutProperty>();
423     return imageLayoutProperty->HasImageSourceInfo() && imageLayoutProperty->GetImageSourceInfoValue().GetSrc() == src;
424 }
425 
426 } // namespace OHOS::Ace::NG
427