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