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_loading_context.h"
17
18 #include "base/utils/utils.h"
19 #include "core/common/container.h"
20 #include "core/components/common/layout/constants.h"
21 #include "core/components_ng/image_provider/drawing_image_data.h"
22 #include "core/components_ng/image_provider/image_utils.h"
23 #include "core/components_ng/image_provider/pixel_map_image_object.h"
24 #include "core/components_ng/image_provider/static_image_object.h"
25 #include "core/components_ng/pattern/image/image_dfx.h"
26 #include "core/components_ng/render/image_painter.h"
27 #include "core/image/image_file_cache.h"
28 #include "core/image/image_loader.h"
29 #include "core/pipeline_ng/pipeline_context.h"
30
31 namespace OHOS::Ace::NG {
32
ImageLoadingContext(const ImageSourceInfo & src,LoadNotifier && loadNotifier,bool syncLoad,bool isSceneBoardWindow,const ImageDfxConfig & imageDfxConfig)33 ImageLoadingContext::ImageLoadingContext(const ImageSourceInfo& src, LoadNotifier&& loadNotifier, bool syncLoad,
34 bool isSceneBoardWindow, const ImageDfxConfig& imageDfxConfig)
35 : src_(src), notifiers_(std::move(loadNotifier)), containerId_(Container::CurrentId()), syncLoad_(syncLoad),
36 isSceneBoardWindow_(isSceneBoardWindow), imageDfxConfig_(imageDfxConfig)
37 {
38 stateManager_ = MakeRefPtr<ImageStateManager>(WeakClaim(this));
39 src_.SetImageDfxConfig(imageDfxConfig_);
40
41 if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE) &&
42 src_.GetSrcType() == SrcType::PIXMAP) {
43 syncLoad_ = true;
44 }
45 }
46
~ImageLoadingContext()47 ImageLoadingContext::~ImageLoadingContext()
48 {
49 if (!syncLoad_) {
50 auto state = stateManager_->GetCurrentState();
51 if (state == ImageLoadingState::DATA_LOADING) {
52 // cancel CreateImgObj task
53 if (!Downloadable()) {
54 ImageProvider::CancelTask(src_.GetTaskKey(), WeakClaim(this));
55 return;
56 }
57 std::string taskKey = src_.GetTaskKey() + (GetOnProgressCallback() ? "1" : "0");
58 if (ImageProvider::CancelTask(taskKey, WeakClaim(this))) {
59 DownloadManager::GetInstance()->RemoveDownloadTaskWithPreload(src_.GetSrc());
60 }
61 } else if (state == ImageLoadingState::MAKE_CANVAS_IMAGE && InstanceOf<StaticImageObject>(imageObj_)) {
62 // cancel MakeCanvasImage task
63 ImageProvider::CancelTask(canvasKey_, WeakClaim(this));
64 }
65 }
66 }
67
CalculateTargetSize(const SizeF & srcSize,const SizeF & dstSize,const SizeF & rawImageSize)68 SizeF ImageLoadingContext::CalculateTargetSize(const SizeF& srcSize, const SizeF& dstSize, const SizeF& rawImageSize)
69 {
70 if (!srcSize.IsPositive()) {
71 return rawImageSize;
72 }
73
74 SizeF targetSize = rawImageSize;
75 auto context = PipelineContext::GetCurrentContext();
76 auto viewScale = context ? context->GetViewScale() : 1.0;
77 double widthScale = dstSize.Width() / srcSize.Width() * viewScale;
78 double heightScale = dstSize.Height() / srcSize.Height() * viewScale;
79 if (widthScale < 1.0 && heightScale < 1.0) {
80 targetSize = SizeF(targetSize.Width() * widthScale, targetSize.Height() * heightScale);
81 }
82 return targetSize;
83 }
84
OnUnloaded()85 void ImageLoadingContext::OnUnloaded()
86 {
87 imageObj_ = nullptr;
88 canvasImage_ = nullptr;
89 srcRect_ = RectF();
90 dstRect_ = RectF();
91 dstSize_ = SizeF();
92 }
93
OnLoadSuccess()94 void ImageLoadingContext::OnLoadSuccess()
95 {
96 if (DynamicCast<StaticImageObject>(imageObj_)) {
97 imageObj_->ClearData();
98 }
99 if (notifiers_.onLoadSuccess_) {
100 notifiers_.onLoadSuccess_(src_);
101 }
102 ImageUtils::PostToUI(std::move(pendingMakeCanvasImageTask_), "ArkUIImageMakeCanvasImage");
103 }
104
OnLoadFail()105 void ImageLoadingContext::OnLoadFail()
106 {
107 if (notifiers_.onLoadFail_) {
108 notifiers_.onLoadFail_(src_, errorMsg_, errorInfo_);
109 }
110 }
111
OnDataReady()112 void ImageLoadingContext::OnDataReady()
113 {
114 if (notifiers_.onDataReady_) {
115 notifiers_.onDataReady_(src_);
116 }
117 }
118
OnDataReadyOnCompleteCallBack()119 void ImageLoadingContext::OnDataReadyOnCompleteCallBack()
120 {
121 if (notifiers_.onDataReadyComplete_) {
122 notifiers_.onDataReadyComplete_(src_);
123 }
124 }
125
SetOnProgressCallback(std::function<void (const uint32_t & dlNow,const uint32_t & dlTotal)> && onProgress)126 void ImageLoadingContext::SetOnProgressCallback(
127 std::function<void(const uint32_t& dlNow, const uint32_t& dlTotal)>&& onProgress)
128 {
129 onProgressCallback_ = onProgress;
130 }
131
OnDataLoading()132 void ImageLoadingContext::OnDataLoading()
133 {
134 auto obj = ImageProvider::QueryImageObjectFromCache(src_);
135 if (obj) {
136 TAG_LOGD(AceLogTag::ACE_IMAGE, "%{private}s obj hit cache", src_.GetSrc().c_str());
137 DataReadyCallback(obj);
138 return;
139 }
140 src_.SetContainerId(containerId_);
141 src_.SetImageDfxConfig(GetImageDfxConfig());
142 ImageProvider::CreateImageObject(src_, WeakClaim(this), syncLoad_, isSceneBoardWindow_);
143 }
144
Downloadable()145 bool ImageLoadingContext::Downloadable()
146 {
147 return src_.GetSrcType() == SrcType::NETWORK && SystemProperties::GetDownloadByNetworkEnabled();
148 }
149
DownloadOnProgress(const uint32_t & dlNow,const uint32_t & dlTotal)150 void ImageLoadingContext::DownloadOnProgress(const uint32_t& dlNow, const uint32_t& dlTotal)
151 {
152 if (onProgressCallback_) {
153 onProgressCallback_(dlNow, dlTotal);
154 }
155 }
156
OnMakeCanvasImage()157 void ImageLoadingContext::OnMakeCanvasImage()
158 {
159 CHECK_NULL_VOID(imageObj_);
160
161 // only update params when entered MakeCanvasImage state successfully
162 if (updateParamsCallback_) {
163 updateParamsCallback_();
164 updateParamsCallback_ = nullptr;
165 }
166 auto userDefinedSize = GetSourceSize();
167 SizeF targetSize;
168 if (userDefinedSize) {
169 ImagePainter::ApplyImageFit(imageFit_, *userDefinedSize, dstSize_, srcRect_, dstRect_);
170 targetSize = *userDefinedSize;
171 } else {
172 auto imageSize = GetImageSize();
173 // calculate the srcRect based on original image size
174 ImagePainter::ApplyImageFit(imageFit_, imageSize, dstSize_, srcRect_, dstRect_);
175
176 bool isPixelMapResource = (SrcType::DATA_ABILITY_DECODED == GetSourceInfo().GetSrcType());
177 if (autoResize_ && !isPixelMapResource) {
178 targetSize = CalculateTargetSize(srcRect_.GetSize(), dstRect_.GetSize(), imageSize);
179 // calculate real srcRect used for paint based on resized image size
180 ImagePainter::ApplyImageFit(imageFit_, targetSize, dstSize_, srcRect_, dstRect_);
181 }
182
183 // upscale targetSize if size level is mapped
184 if (targetSize.IsPositive() && sizeLevel_ > targetSize.Width()) {
185 targetSize.ApplyScale(sizeLevel_ / targetSize.Width());
186 }
187 }
188
189 // step4: [MakeCanvasImage] according to [targetSize]
190 src_.SetImageHdr(GetIsHdrDecoderNeed());
191 canvasKey_ = ImageUtils::GenerateImageKey(src_, targetSize);
192 imageObj_->SetImageSourceInfoHdr(GetIsHdrDecoderNeed());
193 imageObj_->SetImageDfxConfig(imageDfxConfig_);
194 imageObj_->MakeCanvasImage(WeakClaim(this), targetSize, userDefinedSize.has_value(), syncLoad_);
195 }
196
ResizableCalcDstSize()197 void ImageLoadingContext::ResizableCalcDstSize()
198 {
199 auto userDefinedSize = GetSourceSize();
200 if (userDefinedSize) {
201 ImagePainter::ApplyImageFit(imageFit_, *userDefinedSize, dstSize_, srcRect_, dstRect_);
202 return;
203 }
204 auto imageSize = GetImageSize();
205 // calculate the srcRect based on original image size
206 ImagePainter::ApplyImageFit(imageFit_, imageSize, dstSize_, srcRect_, dstRect_);
207
208 bool isPixelMapResource = (SrcType::DATA_ABILITY_DECODED == GetSourceInfo().GetSrcType());
209 if (autoResize_ && !isPixelMapResource) {
210 SizeF targetSize = CalculateTargetSize(srcRect_.GetSize(), dstRect_.GetSize(), imageSize);
211 // calculate real srcRect used for paint based on resized image size
212 ImagePainter::ApplyImageFit(imageFit_, targetSize, dstSize_, srcRect_, dstRect_);
213 }
214 }
215
DataReadyCallback(const RefPtr<ImageObject> & imageObj)216 void ImageLoadingContext::DataReadyCallback(const RefPtr<ImageObject>& imageObj)
217 {
218 CHECK_NULL_VOID(imageObj);
219 imageObj_ = imageObj->Clone();
220 if (measureFinish_) {
221 OnDataReadyOnCompleteCallBack();
222 } else {
223 needDataReadyCallBack_ = true;
224 }
225 stateManager_->HandleCommand(ImageLoadingCommand::LOAD_DATA_SUCCESS);
226 }
227
SuccessCallback(const RefPtr<CanvasImage> & canvasImage)228 void ImageLoadingContext::SuccessCallback(const RefPtr<CanvasImage>& canvasImage)
229 {
230 canvasImage_ = canvasImage;
231 stateManager_->HandleCommand(ImageLoadingCommand::MAKE_CANVAS_IMAGE_SUCCESS);
232 }
233
FailCallback(const std::string & errorMsg,const ImageErrorInfo & errorInfo)234 void ImageLoadingContext::FailCallback(const std::string& errorMsg, const ImageErrorInfo& errorInfo)
235 {
236 errorInfo_ = errorInfo;
237 errorMsg_ = errorMsg;
238 needErrorCallBack_ = true;
239 TAG_LOGD(AceLogTag::ACE_IMAGE, "fail-%{private}s-%{public}s-%{public}s", src_.ToString().c_str(),
240 errorMsg.c_str(), imageDfxConfig_.ToStringWithoutSrc().c_str());
241 CHECK_NULL_VOID(measureFinish_);
242 stateManager_->HandleCommand(ImageLoadingCommand::LOAD_FAIL);
243 needErrorCallBack_ = false;
244 }
245
CallbackAfterMeasureIfNeed()246 void ImageLoadingContext::CallbackAfterMeasureIfNeed()
247 {
248 if (needErrorCallBack_) {
249 stateManager_->HandleCommand(ImageLoadingCommand::LOAD_FAIL);
250 needErrorCallBack_ = false;
251 }
252 if (needDataReadyCallBack_) {
253 OnDataReadyOnCompleteCallBack();
254 needDataReadyCallBack_ = false;
255 }
256 }
257
GetDstRect() const258 const RectF& ImageLoadingContext::GetDstRect() const
259 {
260 return dstRect_;
261 }
262
GetSrcRect() const263 const RectF& ImageLoadingContext::GetSrcRect() const
264 {
265 return srcRect_;
266 }
267
MoveCanvasImage()268 RefPtr<CanvasImage> ImageLoadingContext::MoveCanvasImage()
269 {
270 return std::move(canvasImage_);
271 }
272
MoveImageObject()273 RefPtr<ImageObject> ImageLoadingContext::MoveImageObject()
274 {
275 return std::move(imageObj_);
276 }
277
LoadImageData()278 void ImageLoadingContext::LoadImageData()
279 {
280 stateManager_->HandleCommand(ImageLoadingCommand::LOAD_DATA);
281 }
282
RoundUp(int32_t value)283 int32_t ImageLoadingContext::RoundUp(int32_t value)
284 {
285 CHECK_NULL_RETURN(imageObj_, -1);
286 auto res = imageObj_->GetImageSize().Width();
287 CHECK_NULL_RETURN(value > 0 && res > 0, -1);
288 while (res / 2 >= value) {
289 res /= 2;
290 }
291 return res;
292 }
293
MakeCanvasImageIfNeed(const SizeF & dstSize,bool autoResize,ImageFit imageFit,const std::optional<SizeF> & sourceSize,bool hasValidSlice)294 bool ImageLoadingContext::MakeCanvasImageIfNeed(const SizeF& dstSize, bool autoResize, ImageFit imageFit,
295 const std::optional<SizeF>& sourceSize, bool hasValidSlice)
296 {
297 bool res = autoResize != autoResize_ || imageFit != imageFit_ || sourceSize != GetSourceSize() || firstLoadImage_;
298
299 /* When function is called with a changed dstSize, assume the image will be resized frequently. To minimize
300 * MakeCanvasImage operations, map dstSize to size levels in log_2. Only Remake when the size level changes.
301 */
302 if (SizeChanging(dstSize)) {
303 res |= RoundUp(dstSize.Width()) != sizeLevel_;
304 } else if (dstSize_ == SizeF()) {
305 res |= dstSize.IsPositive();
306 }
307 if (!res && hasValidSlice) {
308 dstSize_ = dstSize;
309 }
310 CHECK_NULL_RETURN(res, res);
311 if (stateManager_->GetCurrentState() == ImageLoadingState::MAKE_CANVAS_IMAGE) {
312 pendingMakeCanvasImageTask_ = [weak = AceType::WeakClaim(this), dstSize, autoResize, imageFit, sourceSize]() {
313 auto ctx = weak.Upgrade();
314 CHECK_NULL_VOID(ctx);
315 CHECK_NULL_VOID(ctx->SizeChanging(dstSize));
316 ctx->MakeCanvasImage(dstSize, autoResize, imageFit, sourceSize);
317 };
318 } else {
319 MakeCanvasImage(dstSize, autoResize, imageFit, sourceSize);
320 }
321 return res;
322 }
323
MakeCanvasImage(const SizeF & dstSize,bool autoResize,ImageFit imageFit,const std::optional<SizeF> & sourceSize)324 void ImageLoadingContext::MakeCanvasImage(
325 const SizeF& dstSize, bool autoResize, ImageFit imageFit, const std::optional<SizeF>& sourceSize)
326 {
327 // Because calling of this interface does not guarantee the execution of [MakeCanvasImage], so in order to avoid
328 // updating params before they are not actually used, capture the params in a function. This function will only run
329 // when it actually do [MakeCanvasImage], i.e. doing the update in [OnMakeCanvasImageTask]
330 updateParamsCallback_ = [wp = WeakClaim(this), dstSize, autoResize, imageFit, sourceSize]() {
331 auto ctx = wp.Upgrade();
332 CHECK_NULL_VOID(ctx);
333 if (ctx->SizeChanging(dstSize) || ctx->firstLoadImage_) {
334 ctx->sizeLevel_ = ctx->RoundUp(dstSize.Width());
335 }
336 ctx->firstLoadImage_ = false;
337 ctx->dstSize_ = dstSize;
338 ctx->imageFit_ = imageFit;
339 ctx->autoResize_ = autoResize;
340 ctx->SetSourceSize(sourceSize);
341 };
342 // send command to [StateManager] and waiting the callback from it to determine next step
343 stateManager_->HandleCommand(ImageLoadingCommand::MAKE_CANVAS_IMAGE);
344 }
345
GetImageSize() const346 SizeF ImageLoadingContext::GetImageSize() const
347 {
348 CHECK_NULL_RETURN(imageObj_, SizeF(-1.0, -1.0));
349 auto imageSize = imageObj_->GetImageSize();
350 auto orientation = imageObj_->GetOrientation();
351 if (orientation == ImageRotateOrientation::LEFT || orientation == ImageRotateOrientation::RIGHT ||
352 orientation == ImageRotateOrientation::LEFT_MIRRORED || orientation == ImageRotateOrientation::RIGHT_MIRRORED) {
353 return { imageSize.Height(), imageSize.Width() };
354 }
355 return imageSize;
356 }
357
GetOriginImageSize() const358 SizeF ImageLoadingContext::GetOriginImageSize() const
359 {
360 return imageObj_ ? imageObj_->GetImageSize() : SizeF(-1, -1);
361 }
362
GetImageFit() const363 ImageFit ImageLoadingContext::GetImageFit() const
364 {
365 return imageFit_;
366 }
367
SetImageFit(ImageFit imageFit)368 void ImageLoadingContext::SetImageFit(ImageFit imageFit)
369 {
370 imageFit_ = imageFit;
371 }
372
GetSourceInfo() const373 const ImageSourceInfo& ImageLoadingContext::GetSourceInfo() const
374 {
375 return src_;
376 }
377
SetAutoResize(bool autoResize)378 void ImageLoadingContext::SetAutoResize(bool autoResize)
379 {
380 autoResize_ = autoResize;
381 }
382
GetDstSize() const383 const SizeF& ImageLoadingContext::GetDstSize() const
384 {
385 return dstSize_;
386 }
387
GetAutoResize() const388 bool ImageLoadingContext::GetAutoResize() const
389 {
390 return autoResize_;
391 }
392
SetSourceSize(const std::optional<SizeF> & sourceSize)393 void ImageLoadingContext::SetSourceSize(const std::optional<SizeF>& sourceSize)
394 {
395 if (sourceSize.has_value()) {
396 sourceSizePtr_ = std::make_unique<SizeF>(sourceSize.value());
397 }
398 }
399
GetSourceSize() const400 std::optional<SizeF> ImageLoadingContext::GetSourceSize() const
401 {
402 CHECK_NULL_RETURN(sourceSizePtr_, std::nullopt);
403 if (sourceSizePtr_->Width() <= 0.0 || sourceSizePtr_->Height() <= 0.0) {
404 TAG_LOGW(AceLogTag::ACE_IMAGE, "Invalid SourceSize, using image size.");
405 return std::nullopt;
406 }
407 return { *sourceSizePtr_ };
408 }
409
NeedAlt() const410 bool ImageLoadingContext::NeedAlt() const
411 {
412 auto state = stateManager_->GetCurrentState();
413 return state != ImageLoadingState::LOAD_SUCCESS;
414 }
415
ResetLoading()416 void ImageLoadingContext::ResetLoading()
417 {
418 stateManager_->HandleCommand(ImageLoadingCommand::RESET_STATE);
419 }
420
ResumeLoading()421 void ImageLoadingContext::ResumeLoading()
422 {
423 stateManager_->HandleCommand(ImageLoadingCommand::LOAD_DATA);
424 }
425
GetCurrentLoadingState()426 const std::string ImageLoadingContext::GetCurrentLoadingState()
427 {
428 ImageLoadingState state = ImageLoadingState::UNLOADED;
429 if (stateManager_) {
430 state = stateManager_->GetCurrentState();
431 }
432 switch (state) {
433 case ImageLoadingState::DATA_LOADING:
434 return "DATA_LOADING";
435 case ImageLoadingState::DATA_READY:
436 return "DATA_READY";
437 case ImageLoadingState::MAKE_CANVAS_IMAGE:
438 return "MAKE_CANVAS_IMAGE";
439 case ImageLoadingState::LOAD_SUCCESS:
440 return "LOAD_SUCCESS";
441 case ImageLoadingState::LOAD_FAIL:
442 return "LOAD_FAIL";
443
444 default:
445 return "UNLOADED";
446 }
447 }
448
GetFrameCount() const449 int32_t ImageLoadingContext::GetFrameCount() const
450 {
451 return imageObj_ ? imageObj_->GetFrameCount() : 0;
452 }
453
GetImageSizeInfo() const454 std::string ImageLoadingContext::GetImageSizeInfo() const
455 {
456 if (!imageObj_) {
457 return "[imageObj=null]";
458 }
459
460 std::ostringstream oss;
461 oss << "[fileSize=" << imageObj_->GetImageFileSize()
462 << ", dataSize=" << imageObj_->GetImageDataSize() << "]";
463 return oss.str();
464 }
465 } // namespace OHOS::Ace::NG
466