1 /*
2 * Copyright (c) 2021 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/image/animated_image_player.h"
17
18 #include "third_party/skia/include/codec/SkCodecAnimation.h"
19 #include "third_party/skia/include/core/SkPixelRef.h"
20
21 #include "base/log/log.h"
22 #include "core/components/image/flutter_render_image.h"
23 #include "core/image/image_provider.h"
24
25 namespace OHOS::Ace {
26
Pause()27 void AnimatedImagePlayer::Pause()
28 {
29 animator_->Pause();
30 }
31
Resume()32 void AnimatedImagePlayer::Resume()
33 {
34 animator_->Resume();
35 }
36
RenderFrame(const int32_t & index)37 void AnimatedImagePlayer::RenderFrame(const int32_t& index)
38 {
39 auto context = context_.Upgrade();
40 if (!context) {
41 LOGW("Context may be destroyed!");
42 return;
43 }
44 auto taskExecutor = context->GetTaskExecutor();
45 taskExecutor->PostTask(
46 [weak = AceType::WeakClaim(this), index, dstWidth = dstWidth_, dstHeight = dstHeight_, taskExecutor] {
47 auto player = weak.Upgrade();
48 if (!player) {
49 return;
50 }
51 auto canvasImage = flutter::CanvasImage::Create();
52 sk_sp<SkImage> skImage = player->DecodeFrameImage(index);
53 if (dstWidth > 0 && dstHeight > 0) {
54 skImage = ImageProvider::ApplySizeToSkImage(skImage, dstWidth, dstHeight);
55 }
56 if (skImage) {
57 canvasImage->set_image({ skImage, player->unrefQueue_ });
58 } else {
59 LOGW("animated player cannot get the %{public}d skImage!", index);
60 return;
61 }
62 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
63 player->successCallback_(player->imageSource_, canvasImage);
64 },
65 TaskExecutor::TaskType::UI);
66 #else
67 taskExecutor->PostTask([callback = player->successCallback_, canvasImage,
68 source = player->imageSource_] { callback(source, canvasImage); },
69 TaskExecutor::TaskType::UI);
70 },
71 TaskExecutor::TaskType::IO);
72 #endif
73
74 }
75
DecodeFrameImage(const int32_t & index)76 sk_sp<SkImage> AnimatedImagePlayer::DecodeFrameImage(const int32_t& index)
77 {
78 // first seek in cache
79 auto iterator = cachedFrame_.find(index);
80 if (iterator != cachedFrame_.end() && iterator->second != nullptr) {
81 LOGD("index %{private}d found in cache.", index);
82 return SkImage::MakeFromBitmap(*iterator->second);
83 }
84
85 SkBitmap bitmap;
86 SkImageInfo info = codec_->getInfo().makeColorType(kN32_SkColorType);
87 bitmap.allocPixels(info);
88 SkCodec::Options options;
89 options.fFrameIndex = index;
90 const int32_t requiredFrame = frameInfos_[index].fRequiredFrame;
91 if (requiredFrame != SkCodec::kNoFrame) {
92 if (requiredFrame == lastRequiredFrameIndex_ && lastRequiredBitmap_ && lastRequiredBitmap_->getPixels() &&
93 CopyTo(&bitmap, lastRequiredBitmap_->colorType(), *lastRequiredBitmap_)) {
94 options.fPriorFrame = requiredFrame;
95 } else if (requiredFrame != lastRequiredFrameIndex_) {
96 // find requiredFrame in cached frame.
97 auto iter = cachedFrame_.find(requiredFrame);
98 if (iter != cachedFrame_.end() && iter->second != nullptr &&
99 CopyTo(&bitmap, iter->second->colorType(), *iter->second)) {
100 options.fPriorFrame = requiredFrame;
101 }
102 }
103 }
104
105 if (SkCodec::kSuccess != codec_->getPixels(info, bitmap.getPixels(), bitmap.rowBytes(), &options)) {
106 LOGW("Could not getPixels for frame %{public}d:", index);
107 return nullptr;
108 }
109
110 if (frameInfos_[index].fDisposalMethod != SkCodecAnimation::DisposalMethod::kRestorePrevious) {
111 lastRequiredBitmap_ = std::make_unique<SkBitmap>(bitmap);
112 lastRequiredFrameIndex_ = index;
113 }
114
115 if (iterator != cachedFrame_.end() && iterator->second == nullptr) {
116 LOGD("index %{private}d cached.", index);
117 iterator->second = std::make_unique<SkBitmap>(bitmap);
118 }
119 #ifndef GPU_DISABLED
120 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM)
121 // weak reference of io manager must be check and used on io thread, because io manager is created on io thread.
122 if (ioManager_) {
123 auto resourceContext = ioManager_->GetResourceContext();
124 if (resourceContext) {
125 SkPixmap pixmap(bitmap.info(), bitmap.pixelRef()->pixels(), bitmap.pixelRef()->rowBytes());
126 return SkImage::MakeCrossContextFromPixmap(resourceContext.get(), pixmap, true, pixmap.colorSpace());
127 }
128 }
129 #endif
130 #endif
131 return SkImage::MakeFromBitmap(bitmap);
132 }
133
CopyTo(SkBitmap * dst,SkColorType dstColorType,const SkBitmap & src)134 bool AnimatedImagePlayer::CopyTo(SkBitmap* dst, SkColorType dstColorType, const SkBitmap& src)
135 {
136 SkPixmap srcPixmap;
137 if (!src.peekPixels(&srcPixmap)) {
138 return false;
139 }
140 SkBitmap tempDstBitmap;
141 SkImageInfo dstInfo = srcPixmap.info().makeColorType(dstColorType);
142 if (!tempDstBitmap.setInfo(dstInfo)) {
143 return false;
144 }
145 if (!tempDstBitmap.tryAllocPixels()) {
146 return false;
147 }
148 SkPixmap dstPixmap;
149 if (!tempDstBitmap.peekPixels(&dstPixmap)) {
150 return false;
151 }
152 if (!srcPixmap.readPixels(dstPixmap)) {
153 return false;
154 }
155 dst->swap(tempDstBitmap);
156 return true;
157 }
158
159 } // namespace OHOS::Ace
160