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