• 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/log/log_wrapper.h"
23 #include "base/memory/referenced.h"
24 #include "base/subwindow/subwindow_manager.h"
25 #include "core/components_ng/image_provider/adapter/image_decoder.h"
26 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
27 #include "core/components_ng/image_provider/animated_image_object.h"
28 #include "core/components_ng/image_provider/image_loading_context.h"
29 #include "core/components_ng/image_provider/image_object.h"
30 #include "core/components_ng/image_provider/image_utils.h"
31 #include "core/components_ng/image_provider/pixel_map_image_object.h"
32 #include "core/components_ng/image_provider/static_image_object.h"
33 #include "core/components_ng/image_provider/svg_image_object.h"
34 #include "core/components_ng/render/adapter/rosen/drawing_image.h"
35 #include "core/image/image_loader.h"
36 #include "core/image/sk_image_cache.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 
39 namespace OHOS::Ace::NG {
40 
CacheImageObject(const RefPtr<ImageObject> & obj)41 void ImageProvider::CacheImageObject(const RefPtr<ImageObject>& obj)
42 {
43     CHECK_NULL_VOID(obj);
44     auto pipelineCtx = PipelineContext::GetCurrentContext();
45     CHECK_NULL_VOID(pipelineCtx);
46     auto cache = pipelineCtx->GetImageCache();
47     CHECK_NULL_VOID(cache);
48     if (cache && obj->IsSupportCache()) {
49         cache->CacheImgObjNG(obj->GetSourceInfo().GetKey(), obj);
50     }
51 }
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     auto imageLoader = ImageLoader::CreateImageLoader(imageObj->GetSourceInfo());
65     CHECK_NULL_RETURN(imageLoader, false);
66 
67     auto container = Container::Current();
68     if (container && container->IsSubContainer()) {
69         TAG_LOGI(AceLogTag::ACE_IMAGE, "subContainer's pipeline's dataProviderManager is null, cannot load image "
70                                        "source, need to switch pipeline in parentContainer.");
71         auto currentId = SubwindowManager::GetInstance()->GetParentContainerId(Container::CurrentId());
72         container = Container::GetContainer(currentId);
73     }
74     CHECK_NULL_RETURN(container, false);
75     auto pipeline = container->GetPipelineContext();
76     CHECK_NULL_RETURN(pipeline, false);
77     auto newLoadedData = imageLoader->GetImageData(imageObj->GetSourceInfo(), WeakClaim(RawPtr(pipeline)));
78     CHECK_NULL_RETURN(newLoadedData, false);
79     // load data success
80     imageObj->SetData(newLoadedData);
81     return true;
82 }
83 
QueryThumbnailCache(const ImageSourceInfo & src)84 RefPtr<ImageObject> ImageProvider::QueryThumbnailCache(const ImageSourceInfo& src)
85 {
86     // query thumbnail from cache
87     auto pipeline = PipelineContext::GetCurrentContext();
88     CHECK_NULL_RETURN(pipeline, nullptr);
89     auto cache = pipeline->GetImageCache();
90     CHECK_NULL_RETURN(cache, nullptr);
91     auto data = DynamicCast<PixmapData>(cache->GetCacheImageData(src.GetKey()));
92     if (data) {
93         return PixelMapImageObject::Create(src, data);
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     return imageObj;
112 }
113 
FailCallback(const std::string & key,const std::string & errorMsg,bool sync)114 void ImageProvider::FailCallback(const std::string& key, const std::string& errorMsg, bool sync)
115 {
116     auto ctxs = EndTask(key);
117     for (auto&& it : ctxs) {
118         auto ctx = it.Upgrade();
119         if (!ctx) {
120             continue;
121         }
122 
123         if (sync) {
124             ctx->FailCallback(errorMsg);
125         } else {
126             // NOTE: contexts may belong to different arkui pipelines
127             auto notifyLoadFailTask = [ctx, errorMsg] { ctx->FailCallback(errorMsg); };
128             ImageUtils::PostToUI(std::move(notifyLoadFailTask), "ArkUIImageProviderFail", ctx->GetContainerId());
129         }
130     }
131 }
132 
SuccessCallback(const RefPtr<CanvasImage> & canvasImage,const std::string & key,bool sync,bool loadInVipChannel)133 void ImageProvider::SuccessCallback(
134     const RefPtr<CanvasImage>& canvasImage, const std::string& key, bool sync, bool loadInVipChannel)
135 {
136     canvasImage->Cache(key);
137     auto ctxs = EndTask(key);
138     // when upload success, pass back canvasImage to LoadingContext
139     for (auto&& it : ctxs) {
140         auto ctx = it.Upgrade();
141         if (!ctx) {
142             continue;
143         }
144         if (sync) {
145             ctx->SuccessCallback(canvasImage->Clone());
146         } else {
147             // NOTE: contexts may belong to different arkui pipelines
148             auto notifyLoadSuccess = [ctx, canvasImage] { ctx->SuccessCallback(canvasImage->Clone()); };
149             ImageUtils::PostToUI(std::move(notifyLoadSuccess), "ArkUIImageProviderSuccess", ctx->GetContainerId());
150         }
151     }
152 }
153 
CreateImageObjHelper(const ImageSourceInfo & src,bool sync)154 void ImageProvider::CreateImageObjHelper(const ImageSourceInfo& src, bool sync)
155 {
156     ACE_SCOPED_TRACE("CreateImageObj %s", src.ToString().c_str());
157     // load image data
158     auto imageLoader = ImageLoader::CreateImageLoader(src);
159     if (!imageLoader) {
160         std::string errorMessage("Failed to create image loader, Image source type not supported");
161         FailCallback(src.GetKey(), src.ToString() + errorMessage, sync);
162         return;
163     }
164     auto pipeline = PipelineContext::GetCurrentContext();
165     RefPtr<ImageData> data = imageLoader->GetImageData(src, WeakClaim(RawPtr(pipeline)));
166     if (!data) {
167         FailCallback(src.GetKey(), "Failed to load image data", sync);
168         return;
169     }
170 
171     // build ImageObject
172     RefPtr<ImageObject> imageObj = ImageProvider::BuildImageObject(src, data);
173     if (!imageObj) {
174         FailCallback(src.GetKey(), "Failed to build image object", sync);
175         return;
176     }
177 
178     auto cloneImageObj = imageObj->Clone();
179 
180     // ImageObject cache is only for saving image size info, clear data to save memory
181     cloneImageObj->ClearData();
182 
183     CacheImageObject(cloneImageObj);
184 
185     auto ctxs = EndTask(src.GetKey());
186     // callback to LoadingContext
187     for (auto&& it : ctxs) {
188         auto ctx = it.Upgrade();
189         if (!ctx) {
190             continue;
191         }
192         if (sync) {
193             ctx->DataReadyCallback(imageObj);
194         } else {
195             // NOTE: contexts may belong to different arkui pipelines
196             auto notifyDataReadyTask = [ctx, imageObj, src] { ctx->DataReadyCallback(imageObj); };
197             ImageUtils::PostToUI(std::move(notifyDataReadyTask), "ArkUIImageProviderDataReady", ctx->GetContainerId());
198         }
199     }
200 }
201 
RegisterTask(const std::string & key,const WeakPtr<ImageLoadingContext> & ctx)202 bool ImageProvider::RegisterTask(const std::string& key, const WeakPtr<ImageLoadingContext>& ctx)
203 {
204     std::scoped_lock<std::mutex> lock(taskMtx_);
205     // key exists -> task is running
206     auto it = tasks_.find(key);
207     if (it != tasks_.end()) {
208         it->second.ctxs_.insert(ctx);
209         return false;
210     }
211     tasks_[key].ctxs_.insert(ctx);
212     return true;
213 }
214 
EndTask(const std::string & key)215 std::set<WeakPtr<ImageLoadingContext>> ImageProvider::EndTask(const std::string& key)
216 {
217     std::scoped_lock<std::mutex> lock(taskMtx_);
218     auto it = tasks_.find(key);
219     if (it == tasks_.end()) {
220         TAG_LOGW(AceLogTag::ACE_IMAGE, "task not found in map %{private}s", key.c_str());
221         return {};
222     }
223     auto ctxs = it->second.ctxs_;
224     if (ctxs.empty()) {
225         TAG_LOGW(AceLogTag::ACE_IMAGE, "registered task has empty context %{public}s", key.c_str());
226     }
227     tasks_.erase(it);
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     auto it = tasks_.find(key);
235     CHECK_NULL_VOID(it != tasks_.end());
236     CHECK_NULL_VOID(it->second.ctxs_.find(ctx) != it->second.ctxs_.end());
237     // only one LoadingContext waiting for this task, can just cancel
238     if (it->second.ctxs_.size() == 1) {
239         // task should be deleted regardless of whether the cancellation is successful or not
240         it->second.bgTask_.Cancel();
241         tasks_.erase(it);
242         return;
243     }
244     // other LoadingContext still waiting for this task, remove ctx from set
245     it->second.ctxs_.erase(ctx);
246 }
247 
CreateImageObject(const ImageSourceInfo & src,const WeakPtr<ImageLoadingContext> & ctxWp,bool sync)248 void ImageProvider::CreateImageObject(const ImageSourceInfo& src, const WeakPtr<ImageLoadingContext>& ctxWp, bool sync)
249 {
250     if (!RegisterTask(src.GetKey(), ctxWp)) {
251         // task is already running, only register callbacks
252         return;
253     }
254     if (sync) {
255         CreateImageObjHelper(src, true);
256     } else {
257         std::scoped_lock<std::mutex> lock(taskMtx_);
258         // wrap with [CancelableCallback] and record in [tasks_] map
259         CancelableCallback<void()> task;
260         task.Reset([src, ctxWp] { ImageProvider::CreateImageObjHelper(src); });
261         tasks_[src.GetKey()].bgTask_ = task;
262         auto ctx = ctxWp.Upgrade();
263         CHECK_NULL_VOID(ctx);
264         ImageUtils::PostToBg(task, "ArkUIImageProviderCreateImageObject", ctx->GetContainerId());
265     }
266 }
267 
BuildImageObject(const ImageSourceInfo & src,const RefPtr<ImageData> & data)268 RefPtr<ImageObject> ImageProvider::BuildImageObject(const ImageSourceInfo& src, const RefPtr<ImageData>& data)
269 {
270     if (!data) {
271         TAG_LOGW(AceLogTag::ACE_IMAGE, "data is null when try ParseImageObjectType, src: %{private}s",
272             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     auto rosenImageData = DynamicCast<DrawingImageData>(data);
284     CHECK_NULL_RETURN(rosenImageData, nullptr);
285     auto [size, frameCount] = rosenImageData->Parse();
286     if (!size.IsPositive()) {
287         TAG_LOGW(AceLogTag::ACE_IMAGE,
288             "Image of src: %{private}s, imageData's size = %{public}d is invalid, and the parsed size is invalid "
289             "%{public}s, "
290             "frameCount is %{public}d",
291             src.ToString().c_str(), static_cast<int32_t>(data->GetSize()), size.ToString().c_str(), frameCount);
292         return nullptr;
293     }
294     if (frameCount > 1) {
295         auto imageObject = MakeRefPtr<AnimatedImageObject>(src, size, data);
296         imageObject->SetFrameCount(frameCount);
297         return imageObject;
298     }
299     return MakeRefPtr<StaticImageObject>(src, size, data);
300 }
301 
MakeCanvasImage(const RefPtr<ImageObject> & obj,const WeakPtr<ImageLoadingContext> & ctxWp,const SizeF & size,const ImageDecoderOptions & imageDecoderOptions)302 void ImageProvider::MakeCanvasImage(const RefPtr<ImageObject>& obj, const WeakPtr<ImageLoadingContext>& ctxWp,
303     const SizeF& size, const ImageDecoderOptions& imageDecoderOptions)
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     if (imageDecoderOptions.sync) {
311         MakeCanvasImageHelper(obj, size, key, imageDecoderOptions);
312     } else {
313         std::scoped_lock<std::mutex> lock(taskMtx_);
314         // wrap with [CancelableCallback] and record in [tasks_] map
315         CancelableCallback<void()> task;
316         task.Reset(
317             [key, obj, size, imageDecoderOptions] { MakeCanvasImageHelper(obj, size, key, imageDecoderOptions); });
318         tasks_[key].bgTask_ = task;
319         auto ctx = ctxWp.Upgrade();
320         CHECK_NULL_VOID(ctx);
321         ImageUtils::PostToBg(task, "ArkUIImageProviderMakeCanvasImage", ctx->GetContainerId());
322     }
323 }
324 
MakeCanvasImageHelper(const RefPtr<ImageObject> & obj,const SizeF & size,const std::string & key,const ImageDecoderOptions & imageDecoderOptions)325 void ImageProvider::MakeCanvasImageHelper(const RefPtr<ImageObject>& obj, const SizeF& size, const std::string& key,
326     const ImageDecoderOptions& imageDecoderOptions)
327 {
328     ImageDecoder decoder(obj, size, imageDecoderOptions.forceResize);
329     RefPtr<CanvasImage> image;
330     // preview and ohos platform
331     if (SystemProperties::GetImageFrameworkEnabled()) {
332         image = decoder.MakePixmapImage(imageDecoderOptions.imageQuality, imageDecoderOptions.isHdrDecoderNeed);
333     } else {
334         image = decoder.MakeDrawingImage();
335     }
336 
337     if (image) {
338         SuccessCallback(image, key, imageDecoderOptions.sync, imageDecoderOptions.loadInVipChannel);
339     } else {
340         FailCallback(key, "Failed to decode image");
341     }
342 }
343 } // namespace OHOS::Ace::NG
344