• 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/common/container.h"
24 #include "core/common/container_scope.h"
25 #include "core/components_ng/image_provider/adapter/skia_image_data.h"
26 #include "core/components_ng/image_provider/animated_image_object.h"
27 #include "core/components_ng/image_provider/image_loading_context.h"
28 #include "core/components_ng/image_provider/image_object.h"
29 #include "core/components_ng/image_provider/image_utils.h"
30 #include "core/components_ng/image_provider/pixel_map_image_object.h"
31 #include "core/components_ng/image_provider/static_image_object.h"
32 #include "core/components_ng/image_provider/svg_image_object.h"
33 #include "core/components_ng/render/adapter/svg_canvas_image.h"
34 #include "core/image/image_loader.h"
35 #include "core/pipeline_ng/pipeline_context.h"
36 
37 namespace OHOS::Ace::NG {
38 
39 namespace {
40 
CacheImageObject(const RefPtr<ImageObject> & obj)41 void CacheImageObject(const RefPtr<ImageObject>& obj)
42 {
43     auto pipelineCtx = PipelineContext::GetCurrentContext();
44     CHECK_NULL_VOID(pipelineCtx);
45     auto cache = pipelineCtx->GetImageCache();
46     CHECK_NULL_VOID(cache);
47     if (cache && obj->IsSupportCache()) {
48         cache->CacheImgObjNG(obj->GetSourceInfo().GetKey(), obj);
49     }
50 }
51 } // namespace
52 
53 std::mutex ImageProvider::taskMtx_;
54 std::unordered_map<std::string, ImageProvider::Task> ImageProvider::tasks_;
55 
PrepareImageData(const RefPtr<ImageObject> & imageObj)56 bool ImageProvider::PrepareImageData(const RefPtr<ImageObject>& imageObj)
57 {
58     CHECK_NULL_RETURN(imageObj, false);
59     // data already loaded
60     if (imageObj->GetData()) {
61         return true;
62     }
63     // if image object has no skData, reload data.
64     std::string errorMessage;
65     do {
66         auto imageLoader = ImageLoader::CreateImageLoader(imageObj->GetSourceInfo());
67         if (!imageLoader) {
68             errorMessage = "Fail to create image loader. Image source type is not supported";
69             break;
70         }
71         auto newLoadedData = imageLoader->GetImageData(
72             imageObj->GetSourceInfo(), WeakClaim(RawPtr(NG::PipelineContext::GetCurrentContext())));
73         if (!newLoadedData) {
74             errorMessage = "Fail to load data, please check if data source is invalid";
75             break;
76         }
77         // load data success
78         imageObj->SetData(newLoadedData);
79         return true;
80     } while (false);
81     return false;
82 }
83 
QueryImageObjectFromCache(const ImageSourceInfo & src)84 RefPtr<ImageObject> ImageProvider::QueryImageObjectFromCache(const ImageSourceInfo& src)
85 {
86     if (!src.IsSupportCache()) {
87         return nullptr;
88     }
89     auto pipelineCtx = PipelineContext::GetCurrentContext();
90     CHECK_NULL_RETURN(pipelineCtx, nullptr);
91     auto imageCache = pipelineCtx->GetImageCache();
92     if (!imageCache) {
93         LOGD("No image cache %{private}s.", src.ToString().c_str());
94         return nullptr;
95     }
96     RefPtr<ImageObject> imageObj = imageCache->GetCacheImgObjNG(src.GetKey());
97     if (imageObj) {
98         LOGD("imageObj found in cache %{private}s", src.ToString().c_str());
99     }
100     return imageObj;
101 }
102 
FailCallback(const std::string & key,const std::string & errorMsg,bool sync)103 void ImageProvider::FailCallback(const std::string& key, const std::string& errorMsg, bool sync)
104 {
105     auto ctxs = EndTask(key);
106     auto notifyLoadFailTask = [ctxs, errorMsg] {
107         for (auto&& it : ctxs) {
108             auto ctx = it.Upgrade();
109             if (!ctx) {
110                 continue;
111             }
112             ctx->FailCallback(errorMsg);
113         }
114     };
115     if (sync) {
116         notifyLoadFailTask();
117     } else {
118         ImageUtils::PostToUI(std::move(notifyLoadFailTask));
119     }
120 }
121 
SuccessCallback(const RefPtr<CanvasImage> & canvasImage,const std::string & key,bool sync)122 void ImageProvider::SuccessCallback(const RefPtr<CanvasImage>& canvasImage, const std::string& key, bool sync)
123 {
124     auto ctxs = EndTask(key);
125     // when upload success, pass back canvasImage to LoadingContext
126     auto notifyLoadSuccess = [ctxs, canvasImage] {
127         for (auto&& it : ctxs) {
128             auto ctx = it.Upgrade();
129             if (!ctx) {
130                 continue;
131             }
132             ctx->SuccessCallback(canvasImage);
133         }
134     };
135     if (sync) {
136         notifyLoadSuccess();
137     } else {
138         ImageUtils::PostToUI(std::move(notifyLoadSuccess));
139     }
140 }
141 
CreateImageObjHelper(const ImageSourceInfo & src,bool sync)142 void ImageProvider::CreateImageObjHelper(const ImageSourceInfo& src, bool sync)
143 {
144     ACE_SCOPED_TRACE("CreateImageObj %s", src.ToString().c_str());
145     // load image data
146     auto imageLoader = ImageLoader::CreateImageLoader(src);
147     if (!imageLoader) {
148         std::string errorMessage("Fail to create image loader, Image source type not supported");
149         FailCallback(src.GetKey(), errorMessage, sync);
150         return;
151     }
152     RefPtr<ImageData> data =
153         imageLoader->GetImageData(src, WeakClaim(RawPtr(NG::PipelineContext::GetCurrentContext())));
154 
155     // build ImageObject
156     RefPtr<ImageObject> imageObj = ImageProvider::BuildImageObject(src, data);
157     if (!imageObj) {
158         FailCallback(src.GetKey(), "Fail to build image object", sync);
159         return;
160     }
161     CacheImageObject(imageObj);
162 
163     auto ctxs = EndTask(src.GetKey());
164     // callback to LoadingContext
165     auto notifyDataReadyTask = [ctxs, imageObj, src] {
166         for (auto&& it : ctxs) {
167             auto ctx = it.Upgrade();
168             if (!ctx) {
169                 continue;
170             }
171             ctx->DataReadyCallback(imageObj);
172         }
173         // ImageObject cache is only for saving image size info, clear data to save memory
174         imageObj->ClearData();
175     };
176     if (sync) {
177         notifyDataReadyTask();
178     } else {
179         ImageUtils::PostToUI(std::move(notifyDataReadyTask));
180     }
181 }
182 
RegisterTask(const std::string & key,const WeakPtr<ImageLoadingContext> & ctx)183 bool ImageProvider::RegisterTask(const std::string& key, const WeakPtr<ImageLoadingContext>& ctx)
184 {
185     std::scoped_lock<std::mutex> lock(taskMtx_);
186     // key exists -> task is running
187     auto it = tasks_.find(key);
188     if (it != tasks_.end()) {
189         it->second.ctxs_.insert(ctx);
190         LOGD("task already exist %{public}s, callbacks size = %u", key.c_str(),
191             static_cast<uint32_t>(it->second.ctxs_.size()));
192         return false;
193     }
194     tasks_[key].ctxs_.insert(ctx);
195     LOGD("task is new %{public}s", key.c_str());
196     return true;
197 }
198 
EndTask(const std::string & key)199 std::set<WeakPtr<ImageLoadingContext>> ImageProvider::EndTask(const std::string& key)
200 {
201     std::scoped_lock<std::mutex> lock(taskMtx_);
202     auto it = tasks_.find(key);
203     if (it == tasks_.end()) {
204         LOGW("task not found in map %{private}s", key.c_str());
205         return std::set<WeakPtr<ImageLoadingContext>>();
206     }
207     auto ctxs = it->second.ctxs_;
208     if (ctxs.empty()) {
209         LOGW("registered task has empty context %{public}s", key.c_str());
210     }
211     tasks_.erase(it);
212     LOGD("endTask %s, ctx size = %u", key.c_str(), static_cast<uint32_t>(ctxs.size()));
213     return ctxs;
214 }
215 
CancelTask(const std::string & key,const WeakPtr<ImageLoadingContext> & ctx)216 void ImageProvider::CancelTask(const std::string& key, const WeakPtr<ImageLoadingContext>& ctx)
217 {
218     std::scoped_lock<std::mutex> lock(taskMtx_);
219     auto it = tasks_.find(key);
220     CHECK_NULL_VOID(it != tasks_.end());
221     CHECK_NULL_VOID(it->second.ctxs_.find(ctx) != it->second.ctxs_.end());
222     // only one LoadingContext waiting for this task, can just cancel
223     if (it->second.ctxs_.size() == 1) {
224         bool canceled = it->second.bgTask_.Cancel();
225         LOGD("cancel bgTask %s, result: %d", key.c_str(), canceled);
226         if (canceled) {
227             tasks_.erase(it);
228             return;
229         }
230     }
231     // other LoadingContext still waiting for this task, remove ctx from set
232     it->second.ctxs_.erase(ctx);
233 }
234 
CreateImageObject(const ImageSourceInfo & src,const WeakPtr<ImageLoadingContext> & ctx,bool sync)235 void ImageProvider::CreateImageObject(const ImageSourceInfo& src, const WeakPtr<ImageLoadingContext>& ctx, bool sync)
236 {
237     if (!RegisterTask(src.GetKey(), ctx)) {
238         // task is already running, only register callbacks
239         return;
240     }
241     if (sync) {
242         CreateImageObjHelper(src, true);
243     } else {
244         std::scoped_lock<std::mutex> lock(taskMtx_);
245         // wrap with [CancelableCallback] and record in [tasks_] map
246         CancelableCallback<void()> task;
247         task.Reset([src] { ImageProvider::CreateImageObjHelper(src); });
248         tasks_[src.GetKey()].bgTask_ = task;
249         ImageUtils::PostToBg(task);
250     }
251 }
252 
BuildImageObject(const ImageSourceInfo & src,const RefPtr<ImageData> & data)253 RefPtr<ImageObject> ImageProvider::BuildImageObject(const ImageSourceInfo& src, const RefPtr<ImageData>& data)
254 {
255     if (!data) {
256         LOGW("data is null when try ParseImageObjectType, src: %{public}s", src.ToString().c_str());
257         return nullptr;
258     }
259     if (src.IsSvg()) {
260         // SVG object needs to make SVG dom during creation
261         return SvgImageObject::Create(src, data);
262     }
263     if (src.IsPixmap()) {
264         return PixelMapImageObject::Create(src, data);
265     }
266 
267     // standard skia image object
268     auto skiaImageData = DynamicCast<SkiaImageData>(data);
269     CHECK_NULL_RETURN(skiaImageData, nullptr);
270     auto [size, frameCount] = skiaImageData->Parse();
271     CHECK_NULL_RETURN(size.IsPositive() && frameCount > 0, nullptr);
272 
273     if (frameCount > 1) {
274         return MakeRefPtr<AnimatedImageObject>(src, size, data);
275     }
276     return MakeRefPtr<StaticImageObject>(src, size, data);
277 }
278 } // namespace OHOS::Ace::NG
279