• 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/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