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