• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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/image/image_provider.h"
17 
18 #ifndef NG_BUILD
19 #include "experimental/svg/model/SkSVGDOM.h"
20 #endif
21 #include "image_compressor.h"
22 #include "third_party/skia/include/core/SkGraphics.h"
23 #include "third_party/skia/include/core/SkStream.h"
24 
25 #include "base/log/ace_trace.h"
26 #include "base/thread/background_task_executor.h"
27 #include "core/common/container.h"
28 #include "core/common/container_scope.h"
29 #include "core/event/ace_event_helper.h"
30 #include "core/image/flutter_image_cache.h"
31 #include "core/image/image_object.h"
32 
33 namespace OHOS::Ace {
34 namespace {
35 
36 // If a picture is a wide color gamut picture, its area value will be larger than this threshold.
37 constexpr double SRGB_GAMUT_AREA = 0.104149;
38 
39 } // namespace
40 
41 std::mutex ImageProvider::loadingImageMutex_;
42 std::unordered_map<std::string, std::vector<LoadCallback>> ImageProvider::loadingImage_;
43 
44 std::mutex ImageProvider::uploadMutex_;
45 std::unordered_map<std::string, std::vector<LoadCallback>> ImageProvider::uploadingImage_;
46 
TrySetLoadingImage(const ImageSourceInfo & imageInfo,const ImageObjSuccessCallback & successCallback,const UploadSuccessCallback & uploadCallback,const FailedCallback & failedCallback)47 bool ImageProvider::TrySetLoadingImage(
48     const ImageSourceInfo& imageInfo,
49     const ImageObjSuccessCallback& successCallback,
50     const UploadSuccessCallback& uploadCallback,
51     const FailedCallback& failedCallback)
52 {
53     std::lock_guard lock(loadingImageMutex_);
54     auto key = imageInfo.GetKey();
55     auto iter = loadingImage_.find(key);
56     if (iter == loadingImage_.end()) {
57         std::vector<LoadCallback> callbacks { { successCallback, uploadCallback, failedCallback } };
58         loadingImage_.emplace(key, callbacks);
59         return true;
60     } else {
61         LOGI("other thread is loading same image: %{public}s", imageInfo.ToString().c_str());
62         iter->second.emplace_back(successCallback, uploadCallback, failedCallback);
63         return false;
64     }
65 }
66 
ProccessLoadingResult(const RefPtr<TaskExecutor> & taskExecutor,const ImageSourceInfo & imageInfo,bool canStartUploadImageObj,const RefPtr<ImageObject> & imageObj,const RefPtr<PipelineBase> & context,const RefPtr<FlutterRenderTaskHolder> & renderTaskHolder,const std::string & errorMsg)67 void ImageProvider::ProccessLoadingResult(
68     const RefPtr<TaskExecutor>& taskExecutor,
69     const ImageSourceInfo& imageInfo,
70     bool canStartUploadImageObj,
71     const RefPtr<ImageObject>& imageObj,
72     const RefPtr<PipelineBase>& context,
73     const RefPtr<FlutterRenderTaskHolder>& renderTaskHolder,
74     const std::string& errorMsg)
75 {
76     std::lock_guard lock(loadingImageMutex_);
77     std::vector<LoadCallback> callbacks;
78     auto key = imageInfo.GetKey();
79     auto iter = loadingImage_.find(key);
80     if (iter != loadingImage_.end()) {
81         std::swap(callbacks, iter->second);
82         for (const auto& callback : callbacks) {
83             if (imageObj == nullptr) {
84                 taskExecutor->PostTask([imageInfo, callback, errorMsg]() {
85                     if (callback.failedCallback) {
86                         callback.failedCallback(imageInfo, errorMsg);
87                     }
88                 }, TaskExecutor::TaskType::UI);
89                 return;
90             }
91             auto obj = imageObj->Clone();
92             taskExecutor->PostTask([obj, imageInfo, callback]() {
93                 if (callback.successCallback) {
94                     callback.successCallback(imageInfo, obj);
95                 }
96             }, TaskExecutor::TaskType::UI);
97             if (canStartUploadImageObj) {
98                 bool forceResize = (!obj->IsSvg()) && (imageInfo.IsSourceDimensionValid());
99                 obj->UploadToGpuForRender(
100                     context,
101                     renderTaskHolder,
102                     callback.uploadCallback,
103                     callback.failedCallback,
104                     obj->GetImageSize(),
105                     forceResize, true);
106             }
107         }
108     } else {
109         LOGW("no loading image: %{public}s", imageInfo.ToString().c_str());
110     }
111     loadingImage_.erase(key);
112 }
113 
TryUploadingImage(const std::string & key,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback)114 bool ImageProvider::TryUploadingImage(
115     const std::string& key,
116     const UploadSuccessCallback& successCallback,
117     const FailedCallback& failedCallback)
118 {
119     std::lock_guard lock(uploadMutex_);
120     auto iter = uploadingImage_.find(key);
121     if (iter == uploadingImage_.end()) {
122         std::vector<LoadCallback> callbacks = { { nullptr, successCallback, failedCallback } };
123         uploadingImage_.emplace(key, callbacks);
124         return true;
125     } else {
126         iter->second.emplace_back(nullptr, successCallback, failedCallback);
127         return false;
128     }
129 }
130 
ProccessUploadResult(const RefPtr<TaskExecutor> & taskExecutor,const ImageSourceInfo & imageInfo,const Size & imageSize,const RefPtr<NG::CanvasImage> & canvasImage,const std::string & errorMsg)131 void ImageProvider::ProccessUploadResult(
132     const RefPtr<TaskExecutor>& taskExecutor,
133     const ImageSourceInfo& imageInfo,
134     const Size& imageSize,
135 #ifdef NG_BUILD
136     const RefPtr<NG::CanvasImage>& canvasImage,
137 #else
138     const fml::RefPtr<flutter::CanvasImage>& canvasImage,
139 #endif
140     const std::string& errorMsg)
141 {
142     std::lock_guard lock(uploadMutex_);
143     std::vector<LoadCallback> callbacks;
144     auto key = ImageObject::GenerateCacheKey(imageInfo, imageSize);
145     auto iter = uploadingImage_.find(key);
146     if (iter != uploadingImage_.end()) {
147         std::swap(callbacks, iter->second);
148         taskExecutor->PostTask([callbacks, imageInfo, canvasImage, errorMsg]() {
149             for (auto callback : callbacks) {
150                 if (canvasImage) {
151                     callback.uploadCallback(imageInfo, canvasImage);
152                 } else {
153                     callback.failedCallback(imageInfo, errorMsg);
154                 }
155             }
156         }, TaskExecutor::TaskType::UI);
157     } else {
158         LOGW("no uploading image: %{public}s", imageInfo.ToString().c_str());
159     }
160     uploadingImage_.erase(key);
161 }
162 
FetchImageObject(const ImageSourceInfo & imageInfo,const ImageObjSuccessCallback & successCallback,const UploadSuccessCallback & uploadSuccessCallback,const FailedCallback & failedCallback,const WeakPtr<PipelineBase> & context,bool syncMode,bool useSkiaSvg,bool needAutoResize,const RefPtr<FlutterRenderTaskHolder> & renderTaskHolder,const OnPostBackgroundTask & onBackgroundTaskPostCallback)163 void ImageProvider::FetchImageObject(
164     const ImageSourceInfo& imageInfo,
165     const ImageObjSuccessCallback& successCallback,
166     const UploadSuccessCallback& uploadSuccessCallback,
167     const FailedCallback& failedCallback,
168     const WeakPtr<PipelineBase>& context,
169     bool syncMode,
170     bool useSkiaSvg,
171     bool needAutoResize,
172     const RefPtr<FlutterRenderTaskHolder>& renderTaskHolder,
173     const OnPostBackgroundTask& onBackgroundTaskPostCallback)
174 {
175     auto task = [context, imageInfo, successCallback, failedCallback, useSkiaSvg, renderTaskHolder,
176                     uploadSuccessCallback, needAutoResize, id = Container::CurrentId(), syncMode]() mutable {
177         ContainerScope scope(id);
178         auto pipelineContext = context.Upgrade();
179         if (!pipelineContext) {
180             LOGE("pipeline context has been released. imageInfo: %{private}s", imageInfo.ToString().c_str());
181             return;
182         }
183         auto taskExecutor = pipelineContext->GetTaskExecutor();
184         if (!taskExecutor) {
185             LOGE("task executor is null. imageInfo: %{private}s", imageInfo.ToString().c_str());
186             return;
187         }
188         if (!syncMode && !TrySetLoadingImage(imageInfo, successCallback, uploadSuccessCallback, failedCallback)) {
189             LOGI("same source is loading: %{private}s", imageInfo.ToString().c_str());
190             return;
191         }
192         RefPtr<ImageObject> imageObj = QueryImageObjectFromCache(imageInfo, pipelineContext);
193         if (!imageObj) { // if image object is not in cache, generate a new one.
194             imageObj = GeneratorAceImageObject(imageInfo, pipelineContext, useSkiaSvg);
195         }
196         if (!imageObj) { // if it fails to generate an image object, trigger fail callback.
197             if (syncMode) {
198                 failedCallback(imageInfo,
199                     "Image data may be broken or absent, please check if image file or image data is valid");
200                 return;
201             }
202             ProccessLoadingResult(taskExecutor, imageInfo, false, nullptr, pipelineContext, renderTaskHolder,
203                 "Image data may be broken or absent, please check if image file or image data is valid.");
204             return;
205         }
206         if (syncMode) {
207             successCallback(imageInfo, imageObj);
208         } else {
209             ProccessLoadingResult(
210                 taskExecutor,
211                 imageInfo,
212                 !needAutoResize && (imageObj->GetFrameCount() == 1),
213                 imageObj,
214                 pipelineContext,
215                 renderTaskHolder);
216         }
217     };
218     if (syncMode) {
219         task();
220         return;
221     }
222     CancelableTask cancelableTask(std::move(task));
223     if (onBackgroundTaskPostCallback) {
224         onBackgroundTaskPostCallback(cancelableTask);
225     }
226     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
227 }
228 
QueryImageObjectFromCache(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> & pipelineContext)229 RefPtr<ImageObject> ImageProvider::QueryImageObjectFromCache(
230     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase>& pipelineContext)
231 {
232     auto imageCache = pipelineContext->GetImageCache();
233     if (!imageCache) {
234         return nullptr;
235     }
236     return imageCache->GetCacheImgObj(imageInfo.ToString());
237 }
238 
GeneratorAceImageObject(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> context,bool useSkiaSvg)239 RefPtr<ImageObject> ImageProvider::GeneratorAceImageObject(
240     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase> context, bool useSkiaSvg)
241 {
242     auto imageData = LoadImageRawData(imageInfo, context);
243 
244     if (!imageData) {
245         LOGE("load image data failed. imageInfo: %{private}s", imageInfo.ToString().c_str());
246         return nullptr;
247     }
248     return ImageObject::BuildImageObject(imageInfo, context, imageData, useSkiaSvg);
249 }
250 
LoadImageRawData(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> context)251 sk_sp<SkData> ImageProvider::LoadImageRawData(
252     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase> context)
253 {
254     ACE_FUNCTION_TRACE();
255     auto imageCache = context->GetImageCache();
256     if (imageCache) {
257         // 1. try get data from cache.
258         auto cacheData = imageCache->GetCacheImageData(imageInfo.GetSrc());
259         if (cacheData) {
260             LOGD("sk data from memory cache.");
261             return AceType::DynamicCast<SkiaCachedImageData>(cacheData)->imageData;
262         }
263     }
264     // 2. try load raw image file.
265     auto imageLoader = ImageLoader::CreateImageLoader(imageInfo);
266     if (!imageLoader) {
267         LOGE("imageLoader create failed. imageInfo: %{private}s", imageInfo.ToString().c_str());
268         return nullptr;
269     }
270     auto data = imageLoader->LoadImageData(imageInfo, context);
271     if (data && imageCache) {
272         // cache sk data.
273         imageCache->CacheImageData(imageInfo.GetSrc(), AceType::MakeRefPtr<SkiaCachedImageData>(data));
274     }
275     return data;
276 }
277 
LoadImageRawDataFromFileCache(const RefPtr<PipelineBase> context,const std::string key,const std::string suffix)278 sk_sp<SkData> ImageProvider::LoadImageRawDataFromFileCache(
279     const RefPtr<PipelineBase> context,
280     const std::string key,
281     const std::string suffix)
282 {
283     ACE_FUNCTION_TRACE();
284     auto imageCache = context->GetImageCache();
285     if (imageCache) {
286         std::string cacheFilePath = ImageCache::GetImageCacheFilePath(key) + suffix;
287         auto data = imageCache->GetDataFromCacheFile(cacheFilePath);
288         if (data) {
289             return AceType::DynamicCast<SkiaCachedImageData>(data)->imageData;
290         }
291     }
292     return nullptr;
293 }
294 
295 #ifndef NG_BUILD
GetSVGImageDOMAsyncFromSrc(const std::string & src,std::function<void (const sk_sp<SkSVGDOM> &)> successCallback,std::function<void ()> failedCallback,const WeakPtr<PipelineBase> context,uint64_t svgThemeColor,OnPostBackgroundTask onBackgroundTaskPostCallback)296 void ImageProvider::GetSVGImageDOMAsyncFromSrc(const std::string& src,
297     std::function<void(const sk_sp<SkSVGDOM>&)> successCallback, std::function<void()> failedCallback,
298     const WeakPtr<PipelineBase> context, uint64_t svgThemeColor, OnPostBackgroundTask onBackgroundTaskPostCallback)
299 {
300     auto task = [src, successCallback, failedCallback, context, svgThemeColor, id = Container::CurrentId()] {
301         ContainerScope scope(id);
302         auto pipelineContext = context.Upgrade();
303         if (!pipelineContext) {
304             LOGW("render image or pipeline has been released.");
305             return;
306         }
307         auto taskExecutor = pipelineContext->GetTaskExecutor();
308         if (!taskExecutor) {
309             return;
310         }
311         ImageSourceInfo info(src);
312         auto imageLoader = ImageLoader::CreateImageLoader(info);
313         if (!imageLoader) {
314             LOGE("load image failed when create image loader.");
315             return;
316         }
317         auto imageData = imageLoader->LoadImageData(info, context);
318         if (imageData) {
319             const auto svgStream = std::make_unique<SkMemoryStream>(std::move(imageData));
320             if (svgStream) {
321                 auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, svgThemeColor);
322                 if (skiaDom) {
323                     taskExecutor->PostTask(
324                         [successCallback, skiaDom] { successCallback(skiaDom); }, TaskExecutor::TaskType::UI);
325                     return;
326                 }
327             }
328         }
329         LOGE("svg data wrong!");
330         taskExecutor->PostTask([failedCallback] { failedCallback(); }, TaskExecutor::TaskType::UI);
331     };
332     CancelableTask cancelableTask(std::move(task));
333     if (onBackgroundTaskPostCallback) {
334         onBackgroundTaskPostCallback(cancelableTask);
335     }
336     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
337 }
338 
GetSVGImageDOMAsyncFromData(const sk_sp<SkData> & skData,std::function<void (const sk_sp<SkSVGDOM> &)> successCallback,std::function<void ()> failedCallback,const WeakPtr<PipelineBase> context,uint64_t svgThemeColor,OnPostBackgroundTask onBackgroundTaskPostCallback)339 void ImageProvider::GetSVGImageDOMAsyncFromData(const sk_sp<SkData>& skData,
340     std::function<void(const sk_sp<SkSVGDOM>&)> successCallback, std::function<void()> failedCallback,
341     const WeakPtr<PipelineBase> context, uint64_t svgThemeColor, OnPostBackgroundTask onBackgroundTaskPostCallback)
342 {
343     auto task = [skData, successCallback, failedCallback, context, svgThemeColor, id = Container::CurrentId()] {
344         ContainerScope scope(id);
345         auto pipelineContext = context.Upgrade();
346         if (!pipelineContext) {
347             LOGW("render image or pipeline has been released.");
348             return;
349         }
350         auto taskExecutor = pipelineContext->GetTaskExecutor();
351         if (!taskExecutor) {
352             return;
353         }
354 
355         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
356         if (svgStream) {
357             auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, svgThemeColor);
358             if (skiaDom) {
359                 taskExecutor->PostTask(
360                     [successCallback, skiaDom] { successCallback(skiaDom); }, TaskExecutor::TaskType::UI);
361                 return;
362             }
363         }
364         LOGE("svg data wrong!");
365         taskExecutor->PostTask([failedCallback] { failedCallback(); }, TaskExecutor::TaskType::UI);
366     };
367     CancelableTask cancelableTask(std::move(task));
368     if (onBackgroundTaskPostCallback) {
369         onBackgroundTaskPostCallback(cancelableTask);
370     }
371     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
372 }
373 #endif
374 
UploadImageToGPUForRender(const WeakPtr<PipelineBase> context,const sk_sp<SkImage> & image,const sk_sp<SkData> & data,const std::function<void (flutter::SkiaGPUObject<SkImage>,sk_sp<SkData>)> && callback,const RefPtr<FlutterRenderTaskHolder> & renderTaskHolder,const std::string src)375 void ImageProvider::UploadImageToGPUForRender(const WeakPtr<PipelineBase> context,
376     const sk_sp<SkImage>& image,
377     const sk_sp<SkData>& data,
378     const std::function<void(flutter::SkiaGPUObject<SkImage>, sk_sp<SkData>)>&& callback,
379     const RefPtr<FlutterRenderTaskHolder>& renderTaskHolder,
380     const std::string src)
381 {
382     if (!renderTaskHolder) {
383         LOGW("renderTaskHolder has been released.");
384         return;
385     }
386 #ifdef UPLOAD_GPU_DISABLED
387     // If want to dump draw command or gpu disabled, should use CPU image.
388     callback({ image, renderTaskHolder->unrefQueue }, nullptr);
389 #else
390     if (data && ImageCompressor::GetInstance()->CanCompress()) {
391         LOGI("use astc cache %{public}s %{public}d * %{public}d", src.c_str(), image->width(), image->height());
392         callback({ image, renderTaskHolder->unrefQueue }, data);
393         return;
394     }
395     auto task = [context, image, callback, renderTaskHolder, src] () {
396         if (!renderTaskHolder) {
397             LOGW("renderTaskHolder has been released.");
398             return;
399         }
400         ACE_DCHECK(!image->isTextureBacked());
401         bool needRaster = ImageCompressor::GetInstance()->CanCompress();
402         if (!needRaster) {
403             callback({ image, renderTaskHolder->unrefQueue }, nullptr);
404             return;
405         } else {
406             auto rasterizedImage = image->isLazyGenerated() ? image->makeRasterImage() : image;
407             if (!rasterizedImage) {
408                 LOGW("Rasterize image failed. callback.");
409                 callback({ image, renderTaskHolder->unrefQueue }, nullptr);
410                 return;
411             }
412             SkPixmap pixmap;
413             if (!rasterizedImage->peekPixels(&pixmap)) {
414                 LOGW("Could not peek pixels of image for texture upload.");
415                 callback({ rasterizedImage, renderTaskHolder->unrefQueue }, nullptr);
416                 return;
417             }
418             int32_t width = static_cast<int32_t>(pixmap.width());
419             int32_t height = static_cast<int32_t>(pixmap.height());
420             sk_sp<SkData> compressData;
421             if (ImageCompressor::GetInstance()->CanCompress()) {
422                 compressData = ImageCompressor::GetInstance()->GpuCompress(src, pixmap, width, height);
423                 ImageCompressor::GetInstance()->WriteToFile(src, compressData, { width, height });
424                 auto pipelineContext = context.Upgrade();
425                 if (pipelineContext && pipelineContext->GetTaskExecutor()) {
426                     auto taskExecutor = pipelineContext->GetTaskExecutor();
427                     taskExecutor->PostDelayedTask(ImageCompressor::GetInstance()->ScheduleReleaseTask(),
428                         TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs);
429                 } else {
430                     BackgroundTaskExecutor::GetInstance().PostTask(
431                         ImageCompressor::GetInstance()->ScheduleReleaseTask());
432                 }
433             }
434             callback({ image, renderTaskHolder->unrefQueue }, compressData);
435             // Trigger purge cpu bitmap resource, after image upload to gpu.
436             SkGraphics::PurgeResourceCache();
437         }
438     };
439     BackgroundTaskExecutor::GetInstance().PostTask(task);
440 #endif
441 }
442 
ResizeSkImage(const sk_sp<SkImage> & rawImage,const std::string & src,Size imageSize,bool forceResize)443 sk_sp<SkImage> ImageProvider::ResizeSkImage(
444     const sk_sp<SkImage>& rawImage, const std::string& src, Size imageSize, bool forceResize)
445 {
446     if (!imageSize.IsValid()) {
447         LOGE("not valid size!, imageSize: %{private}s, src: %{private}s", imageSize.ToString().c_str(), src.c_str());
448         return rawImage;
449     }
450     int32_t dstWidth = static_cast<int32_t>(imageSize.Width() + 0.5);
451     int32_t dstHeight = static_cast<int32_t>(imageSize.Height() + 0.5);
452 
453     bool needResize = false;
454 
455     if (!forceResize) {
456         if (rawImage->width() > dstWidth) {
457             needResize = true;
458         } else {
459             dstWidth = rawImage->width();
460         }
461         if (rawImage->height() > dstHeight) {
462             needResize = true;
463         } else {
464             dstHeight = rawImage->height();
465         }
466     }
467 
468     if (!needResize && !forceResize) {
469         return rawImage;
470     }
471     return ApplySizeToSkImage(
472         rawImage, dstWidth, dstHeight, ImageObject::GenerateCacheKey(ImageSourceInfo(src), imageSize));
473 }
474 
ApplySizeToSkImage(const sk_sp<SkImage> & rawImage,int32_t dstWidth,int32_t dstHeight,const std::string & srcKey)475 sk_sp<SkImage> ImageProvider::ApplySizeToSkImage(
476     const sk_sp<SkImage>& rawImage, int32_t dstWidth, int32_t dstHeight, const std::string& srcKey)
477 {
478     ACE_FUNCTION_TRACE();
479     auto scaledImageInfo =
480         SkImageInfo::Make(dstWidth, dstHeight, rawImage->colorType(), rawImage->alphaType(), rawImage->refColorSpace());
481     SkBitmap scaledBitmap;
482     if (!scaledBitmap.tryAllocPixels(scaledImageInfo)) {
483         LOGE("Could not allocate bitmap when attempting to scale. srcKey: %{private}s, destination size: [%{public}d x"
484              " %{public}d], raw image size: [%{public}d x %{public}d]",
485             srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
486         return rawImage;
487     }
488 #ifdef NG_BUILD
489     if (!rawImage->scalePixels(scaledBitmap.pixmap(), SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
490             SkImage::kDisallow_CachingHint)) {
491 #else
492     if (!rawImage->scalePixels(scaledBitmap.pixmap(), kLow_SkFilterQuality, SkImage::kDisallow_CachingHint)) {
493 #endif
494         LOGE("Could not scale pixels srcKey: %{private}s, destination size: [%{public}d x"
495              " %{public}d], raw image size: [%{public}d x %{public}d]",
496             srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
497         return rawImage;
498     }
499     // Marking this as immutable makes the MakeFromBitmap call share the pixels instead of copying.
500     scaledBitmap.setImmutable();
501     auto scaledImage = SkImage::MakeFromBitmap(scaledBitmap);
502     if (scaledImage) {
503         const double RESIZE_MAX_PROPORTION = ImageCompressor::GetInstance()->CanCompress() ? 1.0 : 0.25;
504         bool needCacheResizedImageFile =
505             (1.0 * dstWidth * dstHeight) / (rawImage->width() * rawImage->height()) < RESIZE_MAX_PROPORTION;
506         if (needCacheResizedImageFile && !srcKey.empty()) {
507             BackgroundTaskExecutor::GetInstance().PostTask(
508                 [srcKey, scaledImage]() {
509                     LOGI("write png cache file: %{private}s", srcKey.c_str());
510                     auto data = scaledImage->encodeToData(SkEncodedImageFormat::kPNG, 100);
511                     if (!data) {
512                         LOGI("encode cache image into cache file failed.");
513                         return;
514                     }
515                     ImageCache::WriteCacheFile(srcKey, data->data(), data->size());
516                 },
517                 BgTaskPriority::LOW);
518         }
519         return scaledImage;
520     }
521     LOGE("Could not create a scaled image from a scaled bitmap. srcKey: %{private}s, destination size: [%{public}d x"
522          " %{public}d], raw image size: [%{public}d x %{public}d]",
523         srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
524     return rawImage;
525 }
526 
527 sk_sp<SkImage> ImageProvider::GetSkImage(const std::string& src, const WeakPtr<PipelineBase> context, Size targetSize)
528 {
529     ImageSourceInfo info(src);
530     auto imageLoader = ImageLoader::CreateImageLoader(info);
531     if (!imageLoader) {
532         LOGE("Invalid src, src is %{public}s", src.c_str());
533         return nullptr;
534     }
535     auto imageSkData = imageLoader->LoadImageData(info, context);
536     if (!imageSkData) {
537         LOGE("fetch data failed. src: %{private}s", src.c_str());
538         return nullptr;
539     }
540     auto rawImage = SkImage::MakeFromEncoded(imageSkData);
541     if (!rawImage) {
542         LOGE("MakeFromEncoded failed! src: %{private}s", src.c_str());
543         return nullptr;
544     }
545     auto image = ResizeSkImage(rawImage, src, targetSize);
546     return image;
547 }
548 
549 void ImageProvider::TryLoadImageInfo(const RefPtr<PipelineBase>& context, const std::string& src,
550     std::function<void(bool, int32_t, int32_t)>&& loadCallback)
551 {
552     BackgroundTaskExecutor::GetInstance().PostTask(
553         [src, callback = std::move(loadCallback), context, id = Container::CurrentId()]() {
554             ContainerScope scope(id);
555             auto taskExecutor = context->GetTaskExecutor();
556             if (!taskExecutor) {
557                 return;
558             }
559             auto image = ImageProvider::GetSkImage(src, context);
560             if (image) {
561                 callback(true, image->width(), image->height());
562                 return;
563             }
564             callback(false, 0, 0);
565         });
566 }
567 
568 bool ImageProvider::IsWideGamut(const sk_sp<SkColorSpace>& colorSpace)
569 {
570     skcms_ICCProfile encodedProfile;
571     if (!colorSpace)
572         return false;
573 
574     colorSpace->toProfile(&encodedProfile);
575     if (!encodedProfile.has_toXYZD50) {
576         LOGI("This profile's gamut can not be represented by a 3x3 transform to XYZD50");
577         return false;
578     }
579     // Normalize gamut by 1.
580     // rgb[3] represents the point of Red, Green and Blue coordinate in color space diagram.
581     Point rgb[3];
582     auto xyzGamut = encodedProfile.toXYZD50;
583     for (int32_t i = 0; i < 3; i++) {
584         auto sum = xyzGamut.vals[i][0] + xyzGamut.vals[i][1] + xyzGamut.vals[i][2];
585         rgb[i].SetX(xyzGamut.vals[i][0] / sum);
586         rgb[i].SetY(xyzGamut.vals[i][1] / sum);
587     }
588     // Calculate the area enclosed by the coordinates of the three RGB points
589     Point red = rgb[0];
590     Point green = rgb[1];
591     Point blue = rgb[2];
592     // Assuming there is a triangle enclosed by three points: A(x1, y1), B(x2, y2), C(x3, y3),
593     // the formula for calculating the area of triangle ABC is as follows:
594     // S = (x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2) / 2.0
595     auto areaOfPoint = std::fabs(red.GetX() * green.GetY() + green.GetX() * blue.GetY() + blue.GetX() * green.GetY() -
596                                  red.GetX() * blue.GetY() - blue.GetX() * green.GetY() - green.GetX() * red.GetY()) /
597                        2.0;
598     return GreatNotEqual(areaOfPoint, SRGB_GAMUT_AREA);
599 }
600 
601 SkImageInfo ImageProvider::MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap>& pixmap)
602 {
603     SkColorType ct = PixelFormatToSkColorType(pixmap);
604     SkAlphaType at = AlphaTypeToSkAlphaType(pixmap);
605     sk_sp<SkColorSpace> cs = ColorSpaceToSkColorSpace(pixmap);
606     return SkImageInfo::Make(pixmap->GetWidth(), pixmap->GetHeight(), ct, at, cs);
607 }
608 
609 sk_sp<SkColorSpace> ImageProvider::ColorSpaceToSkColorSpace(const RefPtr<PixelMap>& pixmap)
610 {
611     return SkColorSpace::MakeSRGB(); // Media::PixelMap has not support wide gamut yet.
612 }
613 
614 SkAlphaType ImageProvider::AlphaTypeToSkAlphaType(const RefPtr<PixelMap>& pixmap)
615 {
616     switch (pixmap->GetAlphaType()) {
617         case AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
618             return SkAlphaType::kUnknown_SkAlphaType;
619         case AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
620             return SkAlphaType::kOpaque_SkAlphaType;
621         case AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
622             return SkAlphaType::kPremul_SkAlphaType;
623         case AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
624             return SkAlphaType::kUnpremul_SkAlphaType;
625         default:
626             return SkAlphaType::kUnknown_SkAlphaType;
627     }
628 }
629 
630 SkColorType ImageProvider::PixelFormatToSkColorType(const RefPtr<PixelMap>& pixmap)
631 {
632     switch (pixmap->GetPixelFormat()) {
633         case PixelFormat::RGB_565:
634             return SkColorType::kRGB_565_SkColorType;
635         case PixelFormat::RGBA_8888:
636             return SkColorType::kRGBA_8888_SkColorType;
637         case PixelFormat::BGRA_8888:
638             return SkColorType::kBGRA_8888_SkColorType;
639         case PixelFormat::ALPHA_8:
640             return SkColorType::kAlpha_8_SkColorType;
641         case PixelFormat::RGBA_F16:
642             return SkColorType::kRGBA_F16_SkColorType;
643         case PixelFormat::UNKNOWN:
644         case PixelFormat::ARGB_8888:
645         case PixelFormat::RGB_888:
646         case PixelFormat::NV21:
647         case PixelFormat::NV12:
648         case PixelFormat::CMYK:
649         default:
650             return SkColorType::kUnknown_SkColorType;
651     }
652 }
653 
654 } // namespace OHOS::Ace
655