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/image_provider/image_provider.h"
17
18 #include <cstdint>
19 #include <mutex>
20
21 #include "base/log/ace_trace.h"
22 #include "base/memory/referenced.h"
23 #include "core/components_ng/image_provider/adapter/image_decoder.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/animated_image_object.h"
30 #include "core/components_ng/image_provider/image_loading_context.h"
31 #include "core/components_ng/image_provider/image_object.h"
32 #include "core/components_ng/image_provider/image_utils.h"
33 #include "core/components_ng/image_provider/pixel_map_image_object.h"
34 #include "core/components_ng/image_provider/static_image_object.h"
35 #include "core/components_ng/image_provider/svg_image_object.h"
36 #ifndef USE_ROSEN_DRAWING
37 #include "core/components_ng/render/adapter/skia_image.h"
38 #else
39 #include "core/components_ng/render/adapter/rosen/drawing_image.h"
40 #endif
41 #include "core/image/image_loader.h"
42 #include "core/image/sk_image_cache.h"
43 #include "core/pipeline_ng/pipeline_context.h"
44
45 namespace OHOS::Ace::NG {
46
CacheImageObject(const RefPtr<ImageObject> & obj)47 void ImageProvider::CacheImageObject(const RefPtr<ImageObject>& obj)
48 {
49 CHECK_NULL_VOID(obj);
50 auto pipelineCtx = PipelineContext::GetCurrentContext();
51 CHECK_NULL_VOID(pipelineCtx);
52 auto cache = pipelineCtx->GetImageCache();
53 CHECK_NULL_VOID(cache);
54 if (cache && obj->IsSupportCache()) {
55 cache->CacheImgObjNG(obj->GetSourceInfo().GetKey(), obj);
56 }
57 }
58
59 std::mutex ImageProvider::taskMtx_;
60 std::unordered_map<std::string, ImageProvider::Task> ImageProvider::tasks_;
61
PrepareImageData(const RefPtr<ImageObject> & imageObj)62 bool ImageProvider::PrepareImageData(const RefPtr<ImageObject>& imageObj)
63 {
64 CHECK_NULL_RETURN(imageObj, false);
65 // data already loaded
66 if (imageObj->GetData()) {
67 return true;
68 }
69 // if image object has no skData, reload data.
70 auto imageLoader = ImageLoader::CreateImageLoader(imageObj->GetSourceInfo());
71 CHECK_NULL_RETURN(imageLoader, false);
72
73 auto pipeline = PipelineContext::GetCurrentContext();
74 auto newLoadedData = imageLoader->GetImageData(imageObj->GetSourceInfo(), WeakClaim(RawPtr(pipeline)));
75 CHECK_NULL_RETURN(newLoadedData, false);
76 // load data success
77 imageObj->SetData(newLoadedData);
78 return true;
79 }
80
QueryThumbnailCache(const ImageSourceInfo & src)81 RefPtr<ImageObject> ImageProvider::QueryThumbnailCache(const ImageSourceInfo& src)
82 {
83 // query thumbnail from cache
84 auto pipeline = PipelineContext::GetCurrentContext();
85 CHECK_NULL_RETURN(pipeline, nullptr);
86 auto cache = pipeline->GetImageCache();
87 CHECK_NULL_RETURN(cache, nullptr);
88 auto data = DynamicCast<PixmapData>(cache->GetCacheImageData(src.GetKey()));
89 if (data) {
90 return PixelMapImageObject::Create(src, data);
91 }
92 return nullptr;
93 }
94
QueryImageObjectFromCache(const ImageSourceInfo & src)95 RefPtr<ImageObject> ImageProvider::QueryImageObjectFromCache(const ImageSourceInfo& src)
96 {
97 if (src.GetSrcType() == SrcType::DATA_ABILITY_DECODED) {
98 return QueryThumbnailCache(src);
99 }
100 if (!src.SupportObjCache()) {
101 return nullptr;
102 }
103 auto pipelineCtx = PipelineContext::GetCurrentContext();
104 CHECK_NULL_RETURN(pipelineCtx, nullptr);
105 auto imageCache = pipelineCtx->GetImageCache();
106 CHECK_NULL_RETURN(imageCache, nullptr);
107 RefPtr<ImageObject> imageObj = imageCache->GetCacheImgObjNG(src.GetKey());
108 return imageObj;
109 }
110
FailCallback(const std::string & key,const std::string & errorMsg,bool sync)111 void ImageProvider::FailCallback(const std::string& key, const std::string& errorMsg, bool sync)
112 {
113 auto ctxs = EndTask(key);
114 auto notifyLoadFailTask = [ctxs, errorMsg] {
115 for (auto&& it : ctxs) {
116 auto ctx = it.Upgrade();
117 if (!ctx) {
118 continue;
119 }
120 ctx->FailCallback(errorMsg);
121 }
122 };
123 if (sync) {
124 notifyLoadFailTask();
125 } else {
126 ImageUtils::PostToUI(std::move(notifyLoadFailTask));
127 }
128 }
129
SuccessCallback(const RefPtr<CanvasImage> & canvasImage,const std::string & key,bool sync)130 void ImageProvider::SuccessCallback(const RefPtr<CanvasImage>& canvasImage, const std::string& key, bool sync)
131 {
132 canvasImage->Cache(key);
133 auto ctxs = EndTask(key);
134 // when upload success, pass back canvasImage to LoadingContext
135 auto notifyLoadSuccess = [ctxs, canvasImage] {
136 for (auto&& it : ctxs) {
137 auto ctx = it.Upgrade();
138 if (!ctx) {
139 continue;
140 }
141 ctx->SuccessCallback(canvasImage->Clone());
142 }
143 };
144 if (sync) {
145 notifyLoadSuccess();
146 } else {
147 ImageUtils::PostToUI(std::move(notifyLoadSuccess));
148 }
149 }
150
CreateImageObjHelper(const ImageSourceInfo & src,bool sync)151 void ImageProvider::CreateImageObjHelper(const ImageSourceInfo& src, bool sync)
152 {
153 ACE_SCOPED_TRACE("CreateImageObj %s", src.ToString().c_str());
154 // load image data
155 auto imageLoader = ImageLoader::CreateImageLoader(src);
156 if (!imageLoader) {
157 std::string errorMessage("Failed to create image loader, Image source type not supported");
158 FailCallback(src.GetKey(), errorMessage, sync);
159 return;
160 }
161 auto pipeline = PipelineContext::GetCurrentContext();
162 RefPtr<ImageData> data = imageLoader->GetImageData(src, WeakClaim(RawPtr(pipeline)));
163 if (!data) {
164 FailCallback(src.GetKey(), "Failed to load image data", sync);
165 return;
166 }
167
168 // build ImageObject
169 RefPtr<ImageObject> imageObj = ImageProvider::BuildImageObject(src, data);
170 if (!imageObj) {
171 FailCallback(src.GetKey(), "Failed to build image object", sync);
172 return;
173 }
174 CacheImageObject(imageObj);
175
176 auto ctxs = EndTask(src.GetKey());
177 // callback to LoadingContext
178 auto notifyDataReadyTask = [ctxs, imageObj, src] {
179 for (auto&& it : ctxs) {
180 auto ctx = it.Upgrade();
181 if (!ctx) {
182 continue;
183 }
184 ctx->DataReadyCallback(imageObj);
185 }
186 // ImageObject cache is only for saving image size info, clear data to save memory
187 imageObj->ClearData();
188 };
189 if (sync) {
190 notifyDataReadyTask();
191 } else {
192 ImageUtils::PostToUI(std::move(notifyDataReadyTask));
193 }
194 }
195
RegisterTask(const std::string & key,const WeakPtr<ImageLoadingContext> & ctx)196 bool ImageProvider::RegisterTask(const std::string& key, const WeakPtr<ImageLoadingContext>& ctx)
197 {
198 std::scoped_lock<std::mutex> lock(taskMtx_);
199 // key exists -> task is running
200 auto it = tasks_.find(key);
201 if (it != tasks_.end()) {
202 it->second.ctxs_.insert(ctx);
203 return false;
204 }
205 tasks_[key].ctxs_.insert(ctx);
206 return true;
207 }
208
EndTask(const std::string & key)209 std::set<WeakPtr<ImageLoadingContext>> ImageProvider::EndTask(const std::string& key)
210 {
211 std::scoped_lock<std::mutex> lock(taskMtx_);
212 auto it = tasks_.find(key);
213 if (it == tasks_.end()) {
214 TAG_LOGW(AceLogTag::ACE_IMAGE, "task not found in map %{private}s", key.c_str());
215 return {};
216 }
217 auto ctxs = it->second.ctxs_;
218 if (ctxs.empty()) {
219 TAG_LOGW(AceLogTag::ACE_IMAGE, "registered task has empty context %{public}s", key.c_str());
220 }
221 tasks_.erase(it);
222 return ctxs;
223 }
224
CancelTask(const std::string & key,const WeakPtr<ImageLoadingContext> & ctx)225 void ImageProvider::CancelTask(const std::string& key, const WeakPtr<ImageLoadingContext>& ctx)
226 {
227 std::scoped_lock<std::mutex> lock(taskMtx_);
228 auto it = tasks_.find(key);
229 CHECK_NULL_VOID(it != tasks_.end());
230 CHECK_NULL_VOID(it->second.ctxs_.find(ctx) != it->second.ctxs_.end());
231 // only one LoadingContext waiting for this task, can just cancel
232 if (it->second.ctxs_.size() == 1) {
233 bool canceled = it->second.bgTask_.Cancel();
234 if (canceled) {
235 tasks_.erase(it);
236 return;
237 }
238 }
239 // other LoadingContext still waiting for this task, remove ctx from set
240 it->second.ctxs_.erase(ctx);
241 }
242
CreateImageObject(const ImageSourceInfo & src,const WeakPtr<ImageLoadingContext> & ctx,bool sync)243 void ImageProvider::CreateImageObject(const ImageSourceInfo& src, const WeakPtr<ImageLoadingContext>& ctx, bool sync)
244 {
245 if (!RegisterTask(src.GetKey(), ctx)) {
246 // task is already running, only register callbacks
247 return;
248 }
249 if (sync) {
250 CreateImageObjHelper(src, true);
251 } else {
252 std::scoped_lock<std::mutex> lock(taskMtx_);
253 // wrap with [CancelableCallback] and record in [tasks_] map
254 CancelableCallback<void()> task;
255 task.Reset([src] { ImageProvider::CreateImageObjHelper(src); });
256 tasks_[src.GetKey()].bgTask_ = task;
257 ImageUtils::PostToBg(task);
258 }
259 }
260
BuildImageObject(const ImageSourceInfo & src,const RefPtr<ImageData> & data)261 RefPtr<ImageObject> ImageProvider::BuildImageObject(const ImageSourceInfo& src, const RefPtr<ImageData>& data)
262 {
263 if (!data) {
264 TAG_LOGW(AceLogTag::ACE_IMAGE, "data is null when try ParseImageObjectType, src: %{public}s",
265 src.ToString().c_str());
266 return nullptr;
267 }
268 if (src.IsSvg()) {
269 // SVG object needs to make SVG dom during creation
270 return SvgImageObject::Create(src, data);
271 }
272 if (src.IsPixmap()) {
273 return PixelMapImageObject::Create(src, data);
274 }
275
276 #ifndef USE_ROSEN_DRAWING
277 auto skiaImageData = DynamicCast<SkiaImageData>(data);
278 CHECK_NULL_RETURN(skiaImageData, nullptr);
279 auto [size, frameCount] = skiaImageData->Parse();
280 #else
281 auto rosenImageData = DynamicCast<DrawingImageData>(data);
282 CHECK_NULL_RETURN(rosenImageData, nullptr);
283 auto [size, frameCount] = rosenImageData->Parse();
284 #endif
285 if (!size.IsPositive()) {
286 TAG_LOGW(AceLogTag::ACE_IMAGE,
287 "Image of src: %{public}s decode failed, imageData's size is invalid %{public}s, frameCount is %{public}d",
288 src.ToString().c_str(), size.ToString().c_str(), frameCount);
289 return nullptr;
290 }
291 if (frameCount > 1) {
292 auto imageObject = MakeRefPtr<AnimatedImageObject>(src, size, data);
293 imageObject->SetFrameCount(frameCount);
294 return imageObject;
295 }
296 return MakeRefPtr<StaticImageObject>(src, size, data);
297 }
298
MakeCanvasImage(const RefPtr<ImageObject> & obj,const WeakPtr<ImageLoadingContext> & ctxWp,const SizeF & size,bool forceResize,bool sync)299 void ImageProvider::MakeCanvasImage(const RefPtr<ImageObject>& obj, const WeakPtr<ImageLoadingContext>& ctxWp,
300 const SizeF& size, bool forceResize, bool sync)
301 {
302 auto key = ImageUtils::GenerateImageKey(obj->GetSourceInfo(), size);
303 // check if same task is already executing
304 if (!RegisterTask(key, ctxWp)) {
305 return;
306 }
307 auto context = ctxWp.Upgrade();
308 if (context && context->Downloadable() && !obj->GetData() && context->GetStateManger()) {
309 EndTask(key);
310 auto stateManager = context->GetStateManger();
311 if (stateManager) {
312 stateManager->SetState(ImageLoadingState::UNLOADED);
313 stateManager->HandleCommand(ImageLoadingCommand::LOAD_DATA);
314 }
315 context->OnDataReady();
316 return;
317 }
318 if (sync) {
319 MakeCanvasImageHelper(obj, size, key, forceResize, true);
320 } else {
321 std::scoped_lock<std::mutex> lock(taskMtx_);
322 // wrap with [CancelableCallback] and record in [tasks_] map
323 CancelableCallback<void()> task;
324 task.Reset([key, obj, size, forceResize] { MakeCanvasImageHelper(obj, size, key, forceResize); });
325 tasks_[key].bgTask_ = task;
326 ImageUtils::PostToBg(task);
327 }
328 }
329
MakeCanvasImageHelper(const RefPtr<ImageObject> & obj,const SizeF & size,const std::string & key,bool forceResize,bool sync)330 void ImageProvider::MakeCanvasImageHelper(
331 const RefPtr<ImageObject>& obj, const SizeF& size, const std::string& key, bool forceResize, bool sync)
332 {
333 ImageDecoder decoder(obj, size, forceResize);
334 RefPtr<CanvasImage> image;
335 if (SystemProperties::GetImageFrameworkEnabled()) {
336 image = decoder.MakePixmapImage();
337 } else {
338 #ifndef USE_ROSEN_DRAWING
339 image = decoder.MakeSkiaImage();
340 #else
341 image = decoder.MakeDrawingImage();
342 #endif
343 }
344
345 if (image) {
346 SuccessCallback(image, key, sync);
347 } else {
348 FailCallback(key, "Failed to decode image");
349 }
350 }
351 } // namespace OHOS::Ace::NG
352