• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2024 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/render/adapter/animated_image.h"
17 
18 #ifdef USE_ROSEN_DRAWING
19 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
20 #include "drawing/engine_adapter/skia_adapter/skia_image_info.h"
21 #endif
22 
23 #ifndef USE_ROSEN_DRAWING
24 #include "core/components_ng/image_provider/adapter/skia_image_data.h"
25 #else
26 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
27 #endif
28 
29 #include "core/components_ng/image_provider/image_utils.h"
30 #include "core/image/sk_image_cache.h"
31 #include "core/pipeline_ng/pipeline_context.h"
32 
33 namespace OHOS::Ace::NG {
34 namespace {
35 constexpr int32_t STANDARD_FRAME_DURATION = 100;
36 constexpr int32_t FORM_REPEAT_COUNT = 1;
37 constexpr float RESIZE_THRESHOLD = 0.7f;
38 
39 struct RSDataWrapper {
40     std::shared_ptr<RSData> data;
41 };
42 
RSDataWrapperReleaseProc(const void *,void * context)43 inline void RSDataWrapperReleaseProc(const void*, void* context)
44 {
45     RSDataWrapper* wrapper = reinterpret_cast<RSDataWrapper*>(context);
46     delete wrapper;
47 }
48 } // namespace
49 
50 #ifndef USE_ROSEN_DRAWING
Create(const RefPtr<SkiaImageData> & data,const ResizeParam & size,const std::string & url)51 RefPtr<CanvasImage> AnimatedImage::Create(
52     const RefPtr<SkiaImageData>& data, const ResizeParam& size, const std::string& url)
53 {
54     CHECK_NULL_RETURN(data, nullptr);
55     auto skData = data->GetSkData();
56     CHECK_NULL_RETURN(skData, nullptr);
57     auto codec = SkCodec::MakeFromData(skData);
58     CHECK_NULL_RETURN(codec, nullptr);
59     if (SystemProperties::GetImageFrameworkEnabled()) {
60         auto src = ImageSource::Create(skData->bytes(), skData->size());
61         CHECK_NULL_RETURN(src, nullptr);
62         return MakeRefPtr<AnimatedPixmap>(codec, src, size, url);
63     }
64     return MakeRefPtr<AnimatedSkImage>(std::move(codec), url);
65 }
66 #else
Create(const RefPtr<DrawingImageData> & data,const ResizeParam & size,const std::string & url)67 RefPtr<CanvasImage> AnimatedImage::Create(
68     const RefPtr<DrawingImageData>& data, const ResizeParam& size, const std::string& url)
69 {
70     CHECK_NULL_RETURN(data, nullptr);
71     auto rsData = data->GetRSData();
72     CHECK_NULL_RETURN(rsData, nullptr);
73     RSDataWrapper* wrapper = new RSDataWrapper { rsData };
74     auto skData = SkData::MakeWithProc(rsData->GetData(), rsData->GetSize(), RSDataWrapperReleaseProc, wrapper);
75     auto codec = SkCodec::MakeFromData(skData);
76     CHECK_NULL_RETURN(codec, nullptr);
77     if (SystemProperties::GetImageFrameworkEnabled()) {
78         auto src = ImageSource::Create(static_cast<const uint8_t*>(rsData->GetData()), rsData->GetSize());
79         CHECK_NULL_RETURN(src, nullptr);
80         return MakeRefPtr<AnimatedPixmap>(codec, src, size, url);
81     }
82     return MakeRefPtr<AnimatedRSImage>(std::move(codec), url);
83 }
84 #endif
85 
AnimatedImage(const std::unique_ptr<SkCodec> & codec,std::string url)86 AnimatedImage::AnimatedImage(const std::unique_ptr<SkCodec>& codec, std::string url)
87     : cacheKey_(std::move(url)), duration_(GenerateDuration(codec)), iteration_(GenerateIteration(codec))
88 {}
89 
90 AnimatedImage::~AnimatedImage() = default;
91 
PostPlayTask(uint32_t idx,int iteration)92 void AnimatedImage::PostPlayTask(uint32_t idx, int iteration)
93 {
94     if (idx == static_cast<uint32_t>(duration_.size())) {
95         iteration--;
96         idx = 0;
97     }
98     if (iteration == 0) {
99         animationState_ = false;
100         return;
101     }
102     RenderFrame(idx);
103     animationState_ = true;
104     currentIdx_ = idx;
105     iteration_ = iteration;
106     currentTask_.Reset([weak = WeakClaim(this), idx, iteration] {
107         auto self = weak.Upgrade();
108         CHECK_NULL_VOID(self);
109         self->PostPlayTask(idx + 1, iteration);
110     });
111     ImageUtils::PostDelayedTaskToUI(currentTask_, duration_[idx], "ArkUIImageAnimatedPostPlayTask");
112 }
113 
GenerateDuration(const std::unique_ptr<SkCodec> & codec)114 std::vector<int> AnimatedImage::GenerateDuration(const std::unique_ptr<SkCodec>& codec)
115 {
116     std::vector<int> duration;
117     auto info = codec->getFrameInfo();
118     for (int32_t i = 0; i < codec->getFrameCount(); ++i) {
119         if (info[i].fDuration <= 0) {
120             duration.push_back(STANDARD_FRAME_DURATION);
121         } else {
122             duration.push_back(info[i].fDuration);
123         }
124     }
125     return duration;
126 }
127 
GenerateIteration(const std::unique_ptr<SkCodec> & codec)128 int AnimatedImage::GenerateIteration(const std::unique_ptr<SkCodec>& codec)
129 {
130     auto iteration = codec->getRepetitionCount() + 1;
131     if (iteration == 0) {
132         iteration = INT_MAX;
133     }
134     auto pipeline = PipelineBase::GetCurrentContext();
135     CHECK_NULL_RETURN(pipeline, 1);
136     if (pipeline->IsFormRender()) {
137         auto container = Container::Current();
138         bool isDynamicComponent = container && container->IsDynamicRender() &&
139                                   container->GetUIContentType() == UIContentType::DYNAMIC_COMPONENT;
140         if (!isDynamicComponent) {
141             iteration = FORM_REPEAT_COUNT;
142         }
143     }
144     return iteration;
145 }
146 
ControlAnimation(bool play)147 void AnimatedImage::ControlAnimation(bool play)
148 {
149     if (play && !animationState_) {
150         PostPlayTask(currentIdx_, iteration_);
151         return;
152     }
153     if (!play && animationState_) {
154         auto result = currentTask_.Cancel();
155         if (result) {
156             animationState_ = false;
157         }
158     }
159 }
160 
GetIsAnimating() const161 bool AnimatedImage::GetIsAnimating() const
162 {
163     return animationState_;
164 }
165 
RenderFrame(uint32_t idx)166 void AnimatedImage::RenderFrame(uint32_t idx)
167 {
168     if (GetCachedFrame(idx)) {
169         return;
170     }
171     ImageUtils::PostToBg(
172         [weak = WeakClaim(this), idx] {
173             auto self = weak.Upgrade();
174             CHECK_NULL_VOID(self);
175             self->DecodeFrame(idx);
176         },
177         "ArkUIImageRenderAnimatedFrame");
178 }
179 
180 // runs on Background threads
DecodeFrame(uint32_t idx)181 void AnimatedImage::DecodeFrame(uint32_t idx)
182 {
183     // max number of decoding thread = 2
184     if (queueSize_ >= 2) {
185         // skip frame
186         return;
187     }
188     ++queueSize_;
189 
190     ACE_SCOPED_TRACE("decode %s frame %d", cacheKey_.c_str(), idx);
191     std::scoped_lock<std::mutex> lock(decodeMtx_);
192     DecodeImpl(idx);
193 
194     ImageUtils::PostToUI(
195         [weak = WeakClaim(this)] {
196             auto self = weak.Upgrade();
197             CHECK_NULL_VOID(self && self->redraw_);
198             self->redraw_();
199         },
200         "ArkUIImageDecodeAnimatedFrame");
201 
202     CacheFrame(cacheKey_ + std::to_string(idx));
203     --queueSize_;
204 }
205 
GetCachedFrame(uint32_t idx)206 bool AnimatedImage::GetCachedFrame(uint32_t idx)
207 {
208     auto image = GetCachedFrameImpl(cacheKey_ + std::to_string(idx));
209     CHECK_NULL_RETURN(image, false);
210 
211     if (!decodeMtx_.try_lock()) {
212         // last frame still decoding, skip this frame to avoid blocking UI thread
213         return true;
214     }
215     UseCachedFrame(std::move(image));
216 
217     decodeMtx_.unlock();
218 
219     if (redraw_) {
220         redraw_();
221     }
222     return true;
223 }
224 
225 // ----------------------------------------------------------
226 // AnimatedSkImage implementation
227 // ----------------------------------------------------------
228 
229 #ifndef USE_ROSEN_DRAWING
GetImage() const230 sk_sp<SkImage> AnimatedSkImage::GetImage() const
231 #else
232 std::shared_ptr<RSImage> AnimatedRSImage::GetImage() const
233 #endif
234 {
235     std::scoped_lock<std::mutex> lock(frameMtx_);
236     return currentFrame_;
237 }
238 
239 #ifndef USE_ROSEN_DRAWING
DecodeImpl(uint32_t idx)240 void AnimatedSkImage::DecodeImpl(uint32_t idx)
241 #else
242 void AnimatedRSImage::DecodeImpl(uint32_t idx)
243 #endif
244 {
245     SkImageInfo imageInfo = codec_->getInfo();
246 #ifndef USE_ROSEN_DRAWING
247     SkBitmap bitmap;
248 #else
249     RSBitmap bitmap;
250 #endif
251 
252     SkCodec::Options options;
253     options.fFrameIndex = static_cast<int32_t>(idx);
254 
255     SkCodec::FrameInfo info {};
256     codec_->getFrameInfo(idx, &info);
257     if (info.fRequiredFrame != SkCodec::kNoFrame) {
258         // frame requires a previous frame as background layer
259         options.fPriorFrame = info.fRequiredFrame;
260         bitmap = requiredFrame_;
261     } else {
262         // create from empty layer
263 #ifndef USE_ROSEN_DRAWING
264         bitmap.allocPixels(imageInfo);
265 #else
266         auto info = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(imageInfo);
267         bitmap.Build(info);
268 #endif
269     }
270 
271     // decode pixels from codec
272 #ifndef USE_ROSEN_DRAWING
273     auto res = codec_->getPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes(), &options);
274 #else
275     auto res = codec_->getPixels(imageInfo, bitmap.GetPixels(), bitmap.GetRowBytes(), &options);
276 #endif
277     CHECK_NULL_VOID(res == SkCodec::kSuccess);
278 
279     // next frame will be drawn on top of this one
280     if (info.fDisposalMethod != SkCodecAnimation::DisposalMethod::kRestorePrevious) {
281         requiredFrame_ = bitmap;
282     }
283 
284     // save current frame, notify redraw
285     {
286         std::scoped_lock<std::mutex> lock(frameMtx_);
287 #ifndef USE_ROSEN_DRAWING
288         currentFrame_ = SkImage::MakeFromBitmap(bitmap);
289 #else
290         currentFrame_ = std::make_shared<RSImage>();
291         currentFrame_->BuildFromBitmap(bitmap);
292 #endif
293     }
294 }
295 
296 #ifndef USE_ROSEN_DRAWING
CacheFrame(const std::string & key)297 void AnimatedSkImage::CacheFrame(const std::string& key)
298 #else
299 void AnimatedRSImage::CacheFrame(const std::string& key)
300 #endif
301 {
302     auto ctx = PipelineContext::GetCurrentContext();
303     CHECK_NULL_VOID(ctx);
304     auto cache = ctx->GetImageCache();
305     CHECK_NULL_VOID(cache);
306     std::shared_ptr<CachedImage> cacheNode;
307     {
308         std::scoped_lock<std::mutex> lock(frameMtx_);
309         cacheNode = std::make_shared<CachedImage>(currentFrame_);
310     }
311     cache->CacheImage(key, cacheNode);
312 }
313 
314 #ifndef USE_ROSEN_DRAWING
GetCachedFrameImpl(const std::string & key)315 RefPtr<CanvasImage> AnimatedSkImage::GetCachedFrameImpl(const std::string& key)
316 {
317     return SkiaImage::QueryFromCache(key);
318 }
319 #else
GetCachedFrameImpl(const std::string & key)320 RefPtr<CanvasImage> AnimatedRSImage::GetCachedFrameImpl(const std::string& key)
321 {
322     return DrawingImage::QueryFromCache(key);
323 }
324 #endif
325 
326 #ifndef USE_ROSEN_DRAWING
UseCachedFrame(RefPtr<CanvasImage> && image)327 void AnimatedSkImage::UseCachedFrame(RefPtr<CanvasImage>&& image)
328 {
329     std::scoped_lock<std::mutex> lock(frameMtx_);
330     currentFrame_ = DynamicCast<SkiaImage>(image)->GetImage();
331 }
332 #else
UseCachedFrame(RefPtr<CanvasImage> && image)333 void AnimatedRSImage::UseCachedFrame(RefPtr<CanvasImage>&& image)
334 {
335     std::scoped_lock<std::mutex> lock(frameMtx_);
336     currentFrame_ = DynamicCast<DrawingImage>(image)->GetImage();
337 }
338 #endif
339 
340 // ----------------------------------------------------------
341 // AnimatedPixmap implementation
342 // ----------------------------------------------------------
AnimatedPixmap(const std::unique_ptr<SkCodec> & codec,const RefPtr<ImageSource> & src,const ResizeParam & size,std::string url)343 AnimatedPixmap::AnimatedPixmap(
344     const std::unique_ptr<SkCodec>& codec, const RefPtr<ImageSource>& src, const ResizeParam& size, std::string url)
345     : AnimatedImage(codec, std::move(url)), size_(size), src_(src)
346 {
347     // resizing to a size >= 0.7 [~= sqrt(2) / 2] intrinsic size takes 2x longer to decode while memory usage is 1/2.
348     // 0.7 is the balance point.
349 }
350 
GetPixelMap() const351 RefPtr<PixelMap> AnimatedPixmap::GetPixelMap() const
352 {
353     std::scoped_lock<std::mutex> lock(frameMtx_);
354     return currentFrame_;
355 }
356 
DecodeImpl(uint32_t idx)357 void AnimatedPixmap::DecodeImpl(uint32_t idx)
358 {
359     if (intrSizeInitial_) {
360         auto intrSize = src_->GetImageSize();
361         if (intrSize.first * RESIZE_THRESHOLD >= size_.width || intrSize.second * RESIZE_THRESHOLD >= size_.height) {
362             size_.forceResize = true;
363         }
364         intrSizeInitial_ = false;
365     }
366     RefPtr<PixelMap> frame;
367     if (size_.forceResize) {
368         frame = src_->CreatePixelMap(idx, { size_.width, size_.height }, size_.imageQuality);
369     } else {
370         // decode to intrinsic size
371         frame = src_->CreatePixelMap(idx, { -1, -1 }, size_.imageQuality);
372     }
373     std::scoped_lock<std::mutex> lock(frameMtx_);
374     currentFrame_ = frame;
375 }
376 
CacheFrame(const std::string & key)377 void AnimatedPixmap::CacheFrame(const std::string& key)
378 {
379     auto ctx = PipelineContext::GetCurrentContext();
380     CHECK_NULL_VOID(ctx);
381     auto cache = ctx->GetImageCache();
382     CHECK_NULL_VOID(cache);
383 
384     std::scoped_lock<std::mutex> lock(frameMtx_);
385     cache->CacheImageData(key, MakeRefPtr<PixmapData>(currentFrame_));
386 }
387 
GetCachedFrameImpl(const std::string & key)388 RefPtr<CanvasImage> AnimatedPixmap::GetCachedFrameImpl(const std::string& key)
389 {
390     return PixelMapImage::QueryFromCache(key);
391 }
392 
UseCachedFrame(RefPtr<CanvasImage> && image)393 void AnimatedPixmap::UseCachedFrame(RefPtr<CanvasImage>&& image)
394 {
395     std::scoped_lock<std::mutex> lock(frameMtx_);
396     currentFrame_ = DynamicCast<PixelMapImage>(image)->GetPixelMap();
397 }
398 } // namespace OHOS::Ace::NG
399