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 #include "image_compressor.h"
19 
20 #ifdef USE_ROSEN_DRAWING
21 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
22 #include "drawing/engine_adapter/skia_adapter/skia_image.h"
23 #include "drawing/engine_adapter/skia_adapter/skia_graphics.h"
24 #endif
25 
26 #include "base/thread/background_task_executor.h"
27 #include "core/common/container.h"
28 #ifdef USE_ROSEN_DRAWING
29 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
30 #include "core/components_ng/render/adapter/rosen/drawing_image.h"
31 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
32 #endif
33 #include "core/image/image_file_cache.h"
34 #include "core/image/image_object.h"
35 
36 namespace OHOS::Ace {
37 namespace {
38 
39 // If a picture is a wide color gamut picture, its area value will be larger than this threshold.
40 constexpr double SRGB_GAMUT_AREA = 0.104149;
41 
42 struct RSDataWrapper {
43     std::shared_ptr<RSData> data;
44 };
45 
RSDataWrapperReleaseProc(const void *,void * context)46 inline void RSDataWrapperReleaseProc(const void*, void* context)
47 {
48     RSDataWrapper* wrapper = reinterpret_cast<RSDataWrapper*>(context);
49     delete wrapper;
50 }
51 } // namespace
52 
53 std::mutex ImageProvider::loadingImageMutex_;
54 std::unordered_map<std::string, std::vector<LoadCallback>> ImageProvider::loadingImage_;
55 
56 std::mutex ImageProvider::uploadMutex_;
57 std::unordered_map<std::string, std::vector<LoadCallback>> ImageProvider::uploadingImage_;
58 
TrySetLoadingImage(const ImageSourceInfo & imageInfo,const ImageObjSuccessCallback & successCallback,const UploadSuccessCallback & uploadCallback,const FailedCallback & failedCallback)59 bool ImageProvider::TrySetLoadingImage(const ImageSourceInfo& imageInfo, const ImageObjSuccessCallback& successCallback,
60     const UploadSuccessCallback& uploadCallback, const FailedCallback& failedCallback)
61 {
62     std::lock_guard lock(loadingImageMutex_);
63     auto key = imageInfo.GetKey();
64     auto iter = loadingImage_.find(key);
65     if (iter == loadingImage_.end()) {
66         std::vector<LoadCallback> callbacks { { successCallback, uploadCallback, failedCallback } };
67         loadingImage_.emplace(key, callbacks);
68         return true;
69     } else {
70         LOGI("other thread is loading same image: %{public}s", imageInfo.ToString().c_str());
71         iter->second.emplace_back(successCallback, uploadCallback, failedCallback);
72         return false;
73     }
74 }
75 
ProccessLoadingResult(const RefPtr<TaskExecutor> & taskExecutor,const ImageSourceInfo & imageInfo,bool canStartUploadImageObj,const RefPtr<ImageObject> & imageObj,const RefPtr<PipelineBase> & context,const std::string & errorMsg)76 void ImageProvider::ProccessLoadingResult(const RefPtr<TaskExecutor>& taskExecutor, const ImageSourceInfo& imageInfo,
77     bool canStartUploadImageObj, const RefPtr<ImageObject>& imageObj, const RefPtr<PipelineBase>& context,
78     const std::string& errorMsg)
79 {
80     std::lock_guard lock(loadingImageMutex_);
81     std::vector<LoadCallback> callbacks;
82     auto key = imageInfo.GetKey();
83     auto iter = loadingImage_.find(key);
84     if (iter != loadingImage_.end()) {
85         std::swap(callbacks, iter->second);
86         for (const auto& callback : callbacks) {
87             if (imageObj == nullptr) {
88                 taskExecutor->PostTask(
89                     [imageInfo, callback, errorMsg]() {
90                         if (callback.failedCallback) {
91                             callback.failedCallback(imageInfo, errorMsg);
92                         }
93                     },
94                     TaskExecutor::TaskType::UI, "ArkUIImageProviderLoadFailed");
95                 return;
96             }
97             auto obj = imageObj->Clone();
98             taskExecutor->PostTask(
99                 [obj, imageInfo, callback]() {
100                     if (callback.successCallback) {
101                         callback.successCallback(imageInfo, obj);
102                     }
103                 },
104                 TaskExecutor::TaskType::UI, "ArkUIImageProviderLoadSuccess");
105             if (canStartUploadImageObj) {
106                 bool forceResize = (!obj->IsSvg()) && (imageInfo.IsSourceDimensionValid());
107                 obj->UploadToGpuForRender(
108                     context, callback.uploadCallback, callback.failedCallback, obj->GetImageSize(), forceResize, true);
109             }
110         }
111     } else {
112         LOGW("no loading image: %{public}s", imageInfo.ToString().c_str());
113     }
114     loadingImage_.erase(key);
115 }
116 
TryUploadingImage(const std::string & key,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback)117 bool ImageProvider::TryUploadingImage(
118     const std::string& key, const UploadSuccessCallback& successCallback, const FailedCallback& failedCallback)
119 {
120     std::lock_guard lock(uploadMutex_);
121     auto iter = uploadingImage_.find(key);
122     if (iter == uploadingImage_.end()) {
123         std::vector<LoadCallback> callbacks = { { nullptr, successCallback, failedCallback } };
124         uploadingImage_.emplace(key, callbacks);
125         return true;
126     } else {
127         iter->second.emplace_back(nullptr, successCallback, failedCallback);
128         return false;
129     }
130 }
131 
ProccessUploadResult(const RefPtr<TaskExecutor> & taskExecutor,const ImageSourceInfo & imageInfo,const Size & imageSize,const RefPtr<NG::CanvasImage> & canvasImage,const std::string & errorMsg)132 void ImageProvider::ProccessUploadResult(const RefPtr<TaskExecutor>& taskExecutor, const ImageSourceInfo& imageInfo,
133     const Size& imageSize, const RefPtr<NG::CanvasImage>& canvasImage, const std::string& errorMsg)
134 {
135     std::lock_guard lock(uploadMutex_);
136     std::vector<LoadCallback> callbacks;
137     auto key = ImageObject::GenerateCacheKey(imageInfo, imageSize);
138     auto iter = uploadingImage_.find(key);
139     if (iter != uploadingImage_.end()) {
140         std::swap(callbacks, iter->second);
141         taskExecutor->PostTask(
142             [callbacks, imageInfo, canvasImage, errorMsg]() {
143                 for (auto callback : callbacks) {
144                     if (canvasImage) {
145                         callback.uploadCallback(imageInfo, canvasImage);
146                     } else {
147                         callback.failedCallback(imageInfo, errorMsg);
148                     }
149                 }
150             },
151             TaskExecutor::TaskType::UI, "ArkUIImageProviderUploadResult");
152     } else {
153         LOGW("no uploading image: %{public}s", imageInfo.ToString().c_str());
154     }
155     uploadingImage_.erase(key);
156 }
157 
FetchImageObject(const ImageSourceInfo & imageInfo,const ImageObjSuccessCallback & successCallback,const UploadSuccessCallback & uploadSuccessCallback,const FailedCallback & failedCallback,const WeakPtr<PipelineBase> & context,bool syncMode,bool useSkiaSvg,bool needAutoResize,const OnPostBackgroundTask & onBackgroundTaskPostCallback)158 void ImageProvider::FetchImageObject(const ImageSourceInfo& imageInfo, const ImageObjSuccessCallback& successCallback,
159     const UploadSuccessCallback& uploadSuccessCallback, const FailedCallback& failedCallback,
160     const WeakPtr<PipelineBase>& context, bool syncMode, bool useSkiaSvg, bool needAutoResize,
161     const OnPostBackgroundTask& onBackgroundTaskPostCallback)
162 {
163     auto task = [context, imageInfo, successCallback, failedCallback, useSkiaSvg, uploadSuccessCallback, needAutoResize,
164                     id = Container::CurrentId(), syncMode]() mutable {
165         ContainerScope scope(id);
166         auto pipelineContext = context.Upgrade();
167         if (!pipelineContext) {
168             LOGE("pipeline context has been released. imageInfo: %{private}s", imageInfo.ToString().c_str());
169             return;
170         }
171         auto taskExecutor = pipelineContext->GetTaskExecutor();
172         if (!taskExecutor) {
173             LOGE("task executor is null. imageInfo: %{private}s", imageInfo.ToString().c_str());
174             return;
175         }
176         if (!syncMode && !TrySetLoadingImage(imageInfo, successCallback, uploadSuccessCallback, failedCallback)) {
177             LOGI("same source is loading: %{private}s", imageInfo.ToString().c_str());
178             return;
179         }
180         RefPtr<ImageObject> imageObj = QueryImageObjectFromCache(imageInfo, pipelineContext);
181         if (!imageObj) { // if image object is not in cache, generate a new one.
182             imageObj = GeneratorAceImageObject(imageInfo, pipelineContext, useSkiaSvg);
183         }
184         if (!imageObj) { // if it fails to generate an image object, trigger fail callback.
185             if (syncMode) {
186                 failedCallback(
187                     imageInfo, "Image data may be broken or absent, please check if image file or image data is valid");
188                 return;
189             }
190             ProccessLoadingResult(taskExecutor, imageInfo, false, nullptr, pipelineContext,
191                 "Image data may be broken or absent, please check if image file or image data is valid.");
192             return;
193         }
194         if (syncMode) {
195             successCallback(imageInfo, imageObj);
196         } else {
197             ProccessLoadingResult(taskExecutor, imageInfo, !needAutoResize && (imageObj->GetFrameCount() == 1),
198                 imageObj, pipelineContext);
199         }
200     };
201     if (syncMode) {
202         task();
203         return;
204     }
205     CancelableTask cancelableTask(std::move(task));
206     if (onBackgroundTaskPostCallback) {
207         onBackgroundTaskPostCallback(cancelableTask);
208     }
209     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
210 }
211 
QueryImageObjectFromCache(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> & pipelineContext)212 RefPtr<ImageObject> ImageProvider::QueryImageObjectFromCache(
213     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase>& pipelineContext)
214 {
215     auto imageCache = pipelineContext->GetImageCache();
216     if (!imageCache) {
217         return nullptr;
218     }
219     return imageCache->GetCacheImgObj(imageInfo.ToString());
220 }
221 
GeneratorAceImageObject(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> context,bool useSkiaSvg)222 RefPtr<ImageObject> ImageProvider::GeneratorAceImageObject(
223     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase> context, bool useSkiaSvg)
224 {
225     auto imageData = LoadImageRawData(imageInfo, context);
226 
227     if (!imageData) {
228         LOGE("load image data failed. imageInfo: %{private}s", imageInfo.ToString().c_str());
229         return nullptr;
230     }
231     return ImageObject::BuildImageObject(imageInfo, context, imageData, useSkiaSvg);
232 }
233 
234 #ifndef USE_ROSEN_DRAWING
LoadImageRawData(const ImageSourceInfo & imageInfo,const RefPtr<PipelineBase> context)235 sk_sp<SkData> ImageProvider::LoadImageRawData(const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase> context)
236 #else
237 std::shared_ptr<RSData> ImageProvider::LoadImageRawData(
238     const ImageSourceInfo& imageInfo, const RefPtr<PipelineBase> context)
239 #endif
240 {
241     ACE_FUNCTION_TRACE();
242     auto imageCache = context->GetImageCache();
243     if (imageCache) {
244         // 1. try get data from cache.
245         auto cacheData = imageCache->GetCacheImageData(imageInfo.GetSrc());
246         if (cacheData) {
247 #ifndef USE_ROSEN_DRAWING
248             const auto* skData = reinterpret_cast<const sk_sp<SkData>*>(cacheData->GetDataWrapper());
249             return *skData;
250 #else
251             return AceType::DynamicCast<NG::DrawingImageData>(cacheData)->GetRSData();
252 #endif
253         }
254     }
255     // 2. try load raw image file.
256     auto imageLoader = ImageLoader::CreateImageLoader(imageInfo);
257     if (!imageLoader) {
258         LOGE("imageLoader create failed. imageInfo: %{private}s", imageInfo.ToString().c_str());
259         return nullptr;
260     }
261     auto data = imageLoader->LoadImageData(imageInfo, context);
262     if (data && imageCache) {
263 #ifndef USE_ROSEN_DRAWING
264         // cache sk data.
265         imageCache->CacheImageData(imageInfo.GetSrc(), NG::ImageData::MakeFromDataWrapper(&data));
266 #else
267         // cache drawing data.
268         imageCache->CacheImageData(imageInfo.GetSrc(), AceType::MakeRefPtr<NG::DrawingImageData>(data));
269 #endif
270     }
271     return data;
272 }
273 
274 #ifndef USE_ROSEN_DRAWING
LoadImageRawDataFromFileCache(const RefPtr<PipelineBase> context,const std::string key,const std::string suffix)275 sk_sp<SkData> ImageProvider::LoadImageRawDataFromFileCache(
276 #else
277 std::shared_ptr<RSData> ImageProvider::LoadImageRawDataFromFileCache(
278 #endif
279     const RefPtr<PipelineBase> context, const std::string key, const std::string suffix)
280 {
281     ACE_FUNCTION_TRACE();
282     auto data = ImageFileCache::GetInstance().GetDataFromCacheFile(key, suffix);
283     if (data) {
284 #ifndef USE_ROSEN_DRAWING
285         const auto* skData = reinterpret_cast<const sk_sp<SkData>*>(data->GetDataWrapper());
286         return *skData;
287 #else
288         return AceType::DynamicCast<NG::DrawingImageData>(data)->GetRSData();
289 #endif
290     }
291     return nullptr;
292 }
293 
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)294 void ImageProvider::GetSVGImageDOMAsyncFromSrc(const std::string& src,
295     std::function<void(const sk_sp<SkSVGDOM>&)> successCallback, std::function<void()> failedCallback,
296     const WeakPtr<PipelineBase> context, uint64_t svgThemeColor, OnPostBackgroundTask onBackgroundTaskPostCallback)
297 {
298     auto task = [src, successCallback, failedCallback, context, svgThemeColor, id = Container::CurrentId()] {
299         ContainerScope scope(id);
300         auto pipelineContext = context.Upgrade();
301         if (!pipelineContext) {
302             LOGW("render image or pipeline has been released.");
303             return;
304         }
305         auto taskExecutor = pipelineContext->GetTaskExecutor();
306         if (!taskExecutor) {
307             return;
308         }
309         ImageSourceInfo info(src);
310         auto imageLoader = ImageLoader::CreateImageLoader(info);
311         if (!imageLoader) {
312             LOGE("load image failed when create image loader.");
313             return;
314         }
315         auto imageData = imageLoader->LoadImageData(info, context);
316         if (imageData) {
317 #ifndef USE_ROSEN_DRAWING
318             const auto svgStream = std::make_unique<SkMemoryStream>(std::move(imageData));
319 #else
320             auto skData = SkData::MakeWithoutCopy(imageData->GetData(), imageData->GetSize());
321             const auto svgStream = std::make_unique<SkMemoryStream>(std::move(skData));
322 #endif
323             if (svgStream) {
324                 auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, svgThemeColor);
325                 if (skiaDom) {
326                     taskExecutor->PostTask(
327                         [successCallback, skiaDom] { successCallback(skiaDom); },
328                         TaskExecutor::TaskType::UI, "ArkUIImageGetSvgDomFromSrcSuccess");
329                     return;
330                 }
331             }
332         }
333         LOGE("svg data wrong!");
334         taskExecutor->PostTask(
335             [failedCallback] { failedCallback(); }, TaskExecutor::TaskType::UI, "ArkUIImageGetSvgDomFromSrcFailed");
336     };
337     CancelableTask cancelableTask(std::move(task));
338     if (onBackgroundTaskPostCallback) {
339         onBackgroundTaskPostCallback(cancelableTask);
340     }
341     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
342 }
343 
344 #ifndef USE_ROSEN_DRAWING
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)345 void ImageProvider::GetSVGImageDOMAsyncFromData(const sk_sp<SkData>& skData,
346 #else
347 void ImageProvider::GetSVGImageDOMAsyncFromData(const std::shared_ptr<RSData>& data,
348 #endif
349     std::function<void(const sk_sp<SkSVGDOM>&)> successCallback, std::function<void()> failedCallback,
350     const WeakPtr<PipelineBase> context, uint64_t svgThemeColor, OnPostBackgroundTask onBackgroundTaskPostCallback)
351 {
352 #ifndef USE_ROSEN_DRAWING
353     auto task = [skData, successCallback, failedCallback, context, svgThemeColor, id = Container::CurrentId()] {
354 #else
355     auto task = [data, successCallback, failedCallback, context, svgThemeColor, id = Container::CurrentId()] {
356 #endif
357         ContainerScope scope(id);
358         auto pipelineContext = context.Upgrade();
359         if (!pipelineContext) {
360             LOGW("render image or pipeline has been released.");
361             return;
362         }
363         auto taskExecutor = pipelineContext->GetTaskExecutor();
364         if (!taskExecutor) {
365             return;
366         }
367 
368 #ifndef USE_ROSEN_DRAWING
369         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
370 #else
371         auto skData = SkData::MakeWithoutCopy(data->GetData(), data->GetSize());
372         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
373 #endif
374         if (svgStream) {
375             auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, svgThemeColor);
376             if (skiaDom) {
377                 taskExecutor->PostTask(
378                     [successCallback, skiaDom] { successCallback(skiaDom); },
379                     TaskExecutor::TaskType::UI, "ArkUIImageGetSvgDomFromDataSuccess");
380                 return;
381             }
382         }
383         LOGE("svg data wrong!");
384         taskExecutor->PostTask(
385             [failedCallback] { failedCallback(); }, TaskExecutor::TaskType::UI, "ArkUIImageGetSvgDomFromDataFailed");
386     };
387     CancelableTask cancelableTask(std::move(task));
388     if (onBackgroundTaskPostCallback) {
389         onBackgroundTaskPostCallback(cancelableTask);
390     }
391     BackgroundTaskExecutor::GetInstance().PostTask(cancelableTask);
392 }
393 
394 #ifndef USE_ROSEN_DRAWING
395 void ImageProvider::UploadImageToGPUForRender(const WeakPtr<PipelineBase> context, const sk_sp<SkImage>& image,
396     const sk_sp<SkData>& data, const std::function<void(sk_sp<SkImage>, sk_sp<SkData>)>&& callback,
397     const std::string src)
398 {
399 #ifdef UPLOAD_GPU_DISABLED
400     // If want to dump draw command or gpu disabled, should use CPU image.
401     callback(image, nullptr);
402 #else
403     if (data && ImageCompressor::GetInstance()->CanCompress()) {
404         LOGI("use astc cache %{public}s %{public}d * %{public}d", src.c_str(), image->width(), image->height());
405         callback(image, data);
406         return;
407     }
408     auto task = [context, image, callback, src]() {
409         ACE_DCHECK(!image->isTextureBacked());
410         bool needRaster = ImageCompressor::GetInstance()->CanCompress();
411         if (!needRaster) {
412             callback(image, nullptr);
413             return;
414         } else {
415             auto rasterizedImage = image->isLazyGenerated() ? image->makeRasterImage() : image;
416             if (!rasterizedImage) {
417                 LOGW("Rasterize image failed. callback.");
418                 callback(image, nullptr);
419                 return;
420             }
421             SkPixmap pixmap;
422             if (!rasterizedImage->peekPixels(&pixmap)) {
423                 LOGW("Could not peek pixels of image for texture upload.");
424                 callback(rasterizedImage, nullptr);
425                 return;
426             }
427             int32_t width = static_cast<int32_t>(pixmap.width());
428             int32_t height = static_cast<int32_t>(pixmap.height());
429             sk_sp<SkData> compressData;
430             if (ImageCompressor::GetInstance()->CanCompress()) {
431                 compressData = ImageCompressor::GetInstance()->GpuCompress(src, pixmap, width, height);
432                 ImageCompressor::GetInstance()->WriteToFile(src, compressData, { width, height });
433                 auto pipelineContext = context.Upgrade();
434                 if (pipelineContext && pipelineContext->GetTaskExecutor()) {
435                     auto taskExecutor = pipelineContext->GetTaskExecutor();
436                     taskExecutor->PostDelayedTask(
437                         ImageCompressor::GetInstance()->ScheduleReleaseTask(), TaskExecutor::TaskType::UI,
438                         ImageCompressor::releaseTimeMs, "ArkUIImageCompressorScheduleRelease");
439                 } else {
440                     BackgroundTaskExecutor::GetInstance().PostTask(
441                         ImageCompressor::GetInstance()->ScheduleReleaseTask());
442                 }
443             }
444             callback(image, compressData);
445             // Trigger purge cpu bitmap resource, after image upload to gpu.
446             SkGraphics::PurgeResourceCache();
447         }
448     };
449     BackgroundTaskExecutor::GetInstance().PostTask(task);
450 #endif
451 }
452 #else
453 void ImageProvider::UploadImageToGPUForRender(const WeakPtr<PipelineBase> context,
454     const std::shared_ptr<RSImage>& image, const std::shared_ptr<RSData>& data,
455     const std::function<void(std::shared_ptr<RSImage>, std::shared_ptr<RSData>)>&& callback, const std::string src)
456 {
457 #ifdef UPLOAD_GPU_DISABLED
458     // If want to dump draw command or gpu disabled, should use CPU image.
459     callback(image, nullptr);
460 #else
461     if (data && ImageCompressor::GetInstance()->CanCompress()) {
462         LOGI("use astc cache %{public}s %{public}d * %{public}d", src.c_str(), image->GetWidth(), image->GetHeight());
463         callback(image, data);
464         return;
465     }
466     auto task = [context, image, callback, src]() {
467         ACE_DCHECK(!image->isTextureBacked());
468         bool needRaster = ImageCompressor::GetInstance()->CanCompress();
469         if (!needRaster) {
470             callback(image, nullptr);
471             return;
472         } else {
473             auto rasterizedImage = image->IsLazyGenerated() ? image->MakeRasterImage() : image;
474             if (!rasterizedImage) {
475                 LOGW("Rasterize image failed. callback.");
476                 callback(image, nullptr);
477                 return;
478             }
479             if (!rasterizedImage->CanPeekPixels()) {
480                 LOGW("Could not peek pixels of image for texture upload.");
481                 callback(rasterizedImage, nullptr);
482                 return;
483             }
484 
485             RSBitmap rsBitmap;
486             RSBitmapFormat rsBitmapFormat { image->GetColorType(), image->GetAlphaType() };
487             rsBitmap.Build(image->GetWidth(), image->GetHeight(), rsBitmapFormat);
488             if (!image->ReadPixels(rsBitmap, 0, 0)) {
489                 callback(image, nullptr);
490                 return;
491             }
492 
493             int32_t width = static_cast<int32_t>(rsBitmap.GetWidth());
494             int32_t height = static_cast<int32_t>(rsBitmap.GetHeight());
495             std::shared_ptr<RSData> compressData;
496             if (ImageCompressor::GetInstance()->CanCompress()) {
497                 compressData = ImageCompressor::GetInstance()->GpuCompress(src, rsBitmap, width, height);
498                 ImageCompressor::GetInstance()->WriteToFile(src, compressData, { width, height });
499                 auto pipelineContext = context.Upgrade();
500                 if (pipelineContext && pipelineContext->GetTaskExecutor()) {
501                     auto taskExecutor = pipelineContext->GetTaskExecutor();
502                     taskExecutor->PostDelayedTask(ImageCompressor::GetInstance()->ScheduleReleaseTask(),
503                         TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs,
504                         "ArkUIImageCompressorScheduleRelease");
505                 } else {
506                     BackgroundTaskExecutor::GetInstance().PostTask(
507                         ImageCompressor::GetInstance()->ScheduleReleaseTask());
508                 }
509             }
510             callback(image, compressData);
511             // Trigger purge cpu bitmap resource, after image upload to gpu.
512             Rosen::Drawing::SkiaGraphics::PurgeResourceCache();
513         }
514     };
515     BackgroundTaskExecutor::GetInstance().PostTask(task);
516 #endif
517 }
518 #endif
519 
520 #ifndef USE_ROSEN_DRAWING
521 sk_sp<SkImage> ImageProvider::ResizeSkImage(
522     const sk_sp<SkImage>& rawImage, const std::string& src, Size imageSize, bool forceResize)
523 {
524     if (!imageSize.IsValid()) {
525         LOGE("not valid size!, imageSize: %{private}s, src: %{private}s", imageSize.ToString().c_str(), src.c_str());
526         return rawImage;
527     }
528     int32_t dstWidth = static_cast<int32_t>(imageSize.Width() + 0.5);
529     int32_t dstHeight = static_cast<int32_t>(imageSize.Height() + 0.5);
530 
531     bool needResize = false;
532 
533     if (!forceResize) {
534         if (rawImage->width() > dstWidth) {
535             needResize = true;
536         } else {
537             dstWidth = rawImage->width();
538         }
539         if (rawImage->height() > dstHeight) {
540             needResize = true;
541         } else {
542             dstHeight = rawImage->height();
543         }
544     }
545 
546     if (!needResize && !forceResize) {
547         return rawImage;
548     }
549     return ApplySizeToSkImage(
550         rawImage, dstWidth, dstHeight, ImageObject::GenerateCacheKey(ImageSourceInfo(src), imageSize));
551 }
552 #else
553 std::shared_ptr<RSImage> ImageProvider::ResizeDrawingImage(
554     const std::shared_ptr<RSImage>& rawImage, const std::string& src, Size imageSize, bool forceResize)
555 {
556     if (!imageSize.IsValid()) {
557         LOGE("not valid size!, imageSize: %{private}s, src: %{private}s", imageSize.ToString().c_str(), src.c_str());
558         return rawImage;
559     }
560     int32_t dstWidth = static_cast<int32_t>(imageSize.Width() + 0.5);
561     int32_t dstHeight = static_cast<int32_t>(imageSize.Height() + 0.5);
562 
563     bool needResize = false;
564 
565     if (!forceResize) {
566         if (rawImage->GetWidth() > dstWidth) {
567             needResize = true;
568         } else {
569             dstWidth = rawImage->GetWidth();
570         }
571         if (rawImage->GetHeight() > dstHeight) {
572             needResize = true;
573         } else {
574             dstHeight = rawImage->GetHeight();
575         }
576     }
577 
578     if (!needResize && !forceResize) {
579         return rawImage;
580     }
581     return ApplySizeToDrawingImage(
582         rawImage, dstWidth, dstHeight, ImageObject::GenerateCacheKey(ImageSourceInfo(src), imageSize));
583 }
584 #endif
585 
586 #ifndef USE_ROSEN_DRAWING
587 sk_sp<SkImage> ImageProvider::ApplySizeToSkImage(
588     const sk_sp<SkImage>& rawImage, int32_t dstWidth, int32_t dstHeight, const std::string& srcKey)
589 {
590     ACE_FUNCTION_TRACE();
591     auto scaledImageInfo =
592         SkImageInfo::Make(dstWidth, dstHeight, rawImage->colorType(), rawImage->alphaType(), rawImage->refColorSpace());
593     SkBitmap scaledBitmap;
594     if (!scaledBitmap.tryAllocPixels(scaledImageInfo)) {
595         LOGE("Could not allocate bitmap when attempting to scale. srcKey: %{private}s, destination size: [%{public}d x"
596              " %{public}d], raw image size: [%{public}d x %{public}d]",
597             srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
598         return rawImage;
599     }
600     if (!rawImage->scalePixels(scaledBitmap.pixmap(), SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone),
601             SkImage::kDisallow_CachingHint)) {
602         LOGE("Could not scale pixels srcKey: %{private}s, destination size: [%{public}d x"
603              " %{public}d], raw image size: [%{public}d x %{public}d]",
604             srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
605         return rawImage;
606     }
607     // Marking this as immutable makes the MakeFromBitmap call share the pixels instead of copying.
608     scaledBitmap.setImmutable();
609     auto scaledImage = SkImage::MakeFromBitmap(scaledBitmap);
610     if (scaledImage) {
611         const double RESIZE_MAX_PROPORTION = ImageCompressor::GetInstance()->CanCompress() ? 1.0 : 0.25;
612         bool needCacheResizedImageFile =
613             (1.0 * dstWidth * dstHeight) / (rawImage->width() * rawImage->height()) < RESIZE_MAX_PROPORTION;
614         auto context = PipelineBase::GetCurrentContext();
615         CHECK_NULL_RETURN(context, scaledImage);
616         // card doesn't encode and cache image file.
617         if (needCacheResizedImageFile && !srcKey.empty() && !context->IsFormRender()) {
618             BackgroundTaskExecutor::GetInstance().PostTask(
619                 [srcKey, scaledImage]() {
620                     LOGI("write png cache file: %{private}s", srcKey.c_str());
621                     auto data = scaledImage->encodeToData(SkEncodedImageFormat::kPNG, 100);
622                     if (!data) {
623                         LOGI("encode cache image into cache file failed.");
624                         return;
625                     }
626                     ImageFileCache::GetInstance().WriteCacheFile(srcKey, data->data(), data->size());
627                 },
628                 BgTaskPriority::LOW);
629         }
630         return scaledImage;
631     }
632     LOGE("Could not create a scaled image from a scaled bitmap. srcKey: %{private}s, destination size: [%{public}d x"
633          " %{public}d], raw image size: [%{public}d x %{public}d]",
634         srcKey.c_str(), dstWidth, dstHeight, rawImage->width(), rawImage->height());
635     return rawImage;
636 }
637 #else
638 std::shared_ptr<RSImage> ImageProvider::ApplySizeToDrawingImage(
639     const std::shared_ptr<RSImage>& rawRSImage, int32_t dstWidth, int32_t dstHeight, const std::string& srcKey)
640 {
641     ACE_FUNCTION_TRACE();
642     RSImageInfo scaledImageInfo { dstWidth, dstHeight,
643         rawRSImage->GetColorType(), rawRSImage->GetAlphaType(), rawRSImage->GetColorSpace() };
644     RSBitmap scaledBitmap;
645     if (!scaledBitmap.TryAllocPixels(scaledImageInfo)) {
646         LOGE("Could not allocate bitmap when attempting to scale. srcKey: %{private}s, destination size: [%{public}d x"
647              " %{public}d], raw image size: [%{public}d x %{public}d]",
648             srcKey.c_str(), dstWidth, dstHeight, rawRSImage->GetWidth(), rawRSImage->GetHeight());
649         return rawRSImage;
650     }
651     if (!rawRSImage->ScalePixels(scaledBitmap, RSSamplingOptions(RSFilterMode::LINEAR, RSMipmapMode::NONE), false)) {
652         LOGE("Could not scale pixels srcKey: %{private}s, destination size: [%{public}d x"
653              " %{public}d], raw image size: [%{public}d x %{public}d]",
654             srcKey.c_str(), dstWidth, dstHeight, rawRSImage->GetWidth(), rawRSImage->GetHeight());
655         return rawRSImage;
656     }
657     // Marking this as immutable makes the MakeFromBitmap call share the pixels instead of copying.
658     scaledBitmap.SetImmutable();
659     std::shared_ptr<RSImage> scaledImage = std::make_shared<RSImage>();
660     if (scaledImage->BuildFromBitmap(scaledBitmap)) {
661         const double RESIZE_MAX_PROPORTION = ImageCompressor::GetInstance()->CanCompress() ? 1.0 : 0.25;
662         bool needCacheResizedImageFile =
663             (1.0 * dstWidth * dstHeight) / (rawRSImage->GetWidth() * rawRSImage->GetHeight()) < RESIZE_MAX_PROPORTION;
664         auto context = PipelineBase::GetCurrentContext();
665         // card doesn't encode and cache image file.
666         if (needCacheResizedImageFile && !srcKey.empty() && !context->IsFormRender()) {
667             BackgroundTaskExecutor::GetInstance().PostTask(
668                 [srcKey, scaledImage]() {
669                     LOGI("write png cache file: %{private}s", srcKey.c_str());
670                     auto data = scaledImage->EncodeToData(RSEncodedImageFormat::PNG, 100);
671                     if (!data) {
672                         return;
673                     }
674                     RSDataWrapper* wrapper = new RSDataWrapper{data};
675                     auto skData = SkData::MakeWithProc(data->GetData(), data->GetSize(),
676                         RSDataWrapperReleaseProc, wrapper);
677                     if (!skData) {
678                         LOGI("encode cache image into cache file failed.");
679                         return;
680                     }
681                     ImageFileCache::GetInstance().WriteCacheFile(srcKey, skData->data(), skData->size());
682                 },
683                 BgTaskPriority::LOW);
684         }
685         return scaledImage;
686     }
687     LOGE("Could not create a scaled image from a scaled bitmap. srcKey: %{private}s, destination size: [%{public}d x"
688          " %{public}d], raw image size: [%{public}d x %{public}d]",
689         srcKey.c_str(), dstWidth, dstHeight, rawRSImage->GetWidth(), rawRSImage->GetHeight());
690     return rawRSImage;
691 }
692 #endif
693 
694 #ifndef USE_ROSEN_DRAWING
695 sk_sp<SkImage> ImageProvider::GetSkImage(const std::string& src, const WeakPtr<PipelineBase> context, Size targetSize)
696 {
697     ImageSourceInfo info(src);
698     auto imageLoader = ImageLoader::CreateImageLoader(info);
699     if (!imageLoader) {
700         LOGE("Invalid src, src is %{public}s", src.c_str());
701         return nullptr;
702     }
703     auto imageSkData = imageLoader->LoadImageData(info, context);
704     if (!imageSkData) {
705         LOGE("fetch data failed. src: %{private}s", src.c_str());
706         return nullptr;
707     }
708     auto rawImage = SkImage::MakeFromEncoded(imageSkData);
709     if (!rawImage) {
710         LOGE("MakeFromEncoded failed! src: %{private}s", src.c_str());
711         return nullptr;
712     }
713     auto image = ResizeSkImage(rawImage, src, targetSize);
714     return image;
715 }
716 #else
717 std::shared_ptr<RSImage> ImageProvider::GetDrawingImage(
718     const std::string& src, const WeakPtr<PipelineBase> context, Size targetSize)
719 {
720     ImageSourceInfo info(src);
721     auto imageLoader = ImageLoader::CreateImageLoader(info);
722     if (!imageLoader) {
723         LOGE("Invalid src, src is %{public}s", src.c_str());
724         return nullptr;
725     }
726     auto imageData = imageLoader->LoadImageData(info, context);
727     if (!imageData) {
728         LOGE("fetch data failed. src: %{private}s", src.c_str());
729         return nullptr;
730     }
731     std::shared_ptr<RSImage> rawImage = std::make_shared<RSImage>();
732     if (!rawImage->MakeFromEncoded(imageData)) {
733         LOGE("MakeFromEncoded failed! src: %{private}s", src.c_str());
734         return nullptr;
735     }
736     auto image = ResizeDrawingImage(rawImage, src, targetSize);
737     return image;
738 }
739 #endif
740 
741 void ImageProvider::TryLoadImageInfo(const RefPtr<PipelineBase>& context, const std::string& src,
742     std::function<void(bool, int32_t, int32_t)>&& loadCallback)
743 {
744     BackgroundTaskExecutor::GetInstance().PostTask(
745         [src, callback = std::move(loadCallback), context, id = Container::CurrentId()]() {
746             ContainerScope scope(id);
747             auto taskExecutor = context->GetTaskExecutor();
748             if (!taskExecutor) {
749                 return;
750             }
751 #ifndef USE_ROSEN_DRAWING
752             auto image = ImageProvider::GetSkImage(src, context);
753             if (image) {
754                 callback(true, image->width(), image->height());
755                 return;
756             }
757 #else
758             auto image = ImageProvider::GetDrawingImage(src, context);
759             if (image) {
760                 callback(true, image->GetWidth(), image->GetHeight());
761                 return;
762             }
763 #endif
764             callback(false, 0, 0);
765         });
766 }
767 
768 #ifndef USE_ROSEN_DRAWING
769 bool ImageProvider::IsWideGamut(const sk_sp<SkColorSpace>& colorSpace)
770 {
771 #else
772 bool ImageProvider::IsWideGamut(const std::shared_ptr<RSColorSpace>& rsColorSpace)
773 {
774     if (!rsColorSpace) {
775         return false;
776     }
777     auto colorSpace = rsColorSpace->GetSkColorSpace();
778 #endif
779     if (!colorSpace)
780         return false;
781 
782     skcms_ICCProfile encodedProfile;
783     colorSpace->toProfile(&encodedProfile);
784     if (!encodedProfile.has_toXYZD50) {
785         LOGI("This profile's gamut can not be represented by a 3x3 transform to XYZD50");
786         return false;
787     }
788     // Normalize gamut by 1.
789     // rgb[3] represents the point of Red, Green and Blue coordinate in color space diagram.
790     Point rgb[3];
791     auto xyzGamut = encodedProfile.toXYZD50;
792     for (int32_t i = 0; i < 3; i++) {
793         auto sum = xyzGamut.vals[i][0] + xyzGamut.vals[i][1] + xyzGamut.vals[i][2];
794         rgb[i].SetX(xyzGamut.vals[i][0] / sum);
795         rgb[i].SetY(xyzGamut.vals[i][1] / sum);
796     }
797     // Calculate the area enclosed by the coordinates of the three RGB points
798     Point red = rgb[0];
799     Point green = rgb[1];
800     Point blue = rgb[2];
801     // Assuming there is a triangle enclosed by three points: A(x1, y1), B(x2, y2), C(x3, y3),
802     // the formula for calculating the area of triangle ABC is as follows:
803     // S = (x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2) / 2.0
804     auto areaOfPoint = std::fabs(red.GetX() * green.GetY() + green.GetX() * blue.GetY() + blue.GetX() * green.GetY() -
805                                  red.GetX() * blue.GetY() - blue.GetX() * green.GetY() - green.GetX() * red.GetY()) /
806                        2.0;
807     return GreatNotEqual(areaOfPoint, SRGB_GAMUT_AREA);
808 }
809 
810 #ifndef USE_ROSEN_DRAWING
811 SkImageInfo ImageProvider::MakeSkImageInfoFromPixelMap(const RefPtr<PixelMap>& pixmap)
812 {
813     SkColorType ct = PixelFormatToSkColorType(pixmap);
814     SkAlphaType at = AlphaTypeToSkAlphaType(pixmap);
815     sk_sp<SkColorSpace> cs = ColorSpaceToSkColorSpace(pixmap);
816     return SkImageInfo::Make(pixmap->GetWidth(), pixmap->GetHeight(), ct, at, cs);
817 }
818 #else
819 RSBitmapFormat ImageProvider::MakeRSBitmapFormatFromPixelMap(const RefPtr<PixelMap>& pixmap)
820 {
821     return { PixelFormatToDrawingColorType(pixmap), AlphaTypeToDrawingAlphaType(pixmap) };
822 }
823 RSImageInfo ImageProvider::MakeRSImageInfoFromPixelMap(const RefPtr<PixelMap>& pixmap)
824 {
825     RSColorType ct = PixelFormatToDrawingColorType(pixmap);
826     RSAlphaType at = AlphaTypeToDrawingAlphaType(pixmap);
827     std::shared_ptr<RSColorSpace> cs = ColorSpaceToDrawingColorSpace(pixmap);
828     return { pixmap->GetWidth(), pixmap->GetHeight(), ct, at, cs };
829 }
830 #endif
831 
832 #ifndef USE_ROSEN_DRAWING
833 sk_sp<SkColorSpace> ImageProvider::ColorSpaceToSkColorSpace(const RefPtr<PixelMap>& pixmap)
834 {
835     return SkColorSpace::MakeSRGB(); // Media::PixelMap has not support wide gamut yet.
836 }
837 #else
838 std::shared_ptr<RSColorSpace> ImageProvider::ColorSpaceToDrawingColorSpace(const RefPtr<PixelMap>& pixmap)
839 {
840     return RSColorSpace::CreateSRGB(); // Media::PixelMap has not support wide gamut yet.
841 }
842 #endif
843 
844 #ifndef USE_ROSEN_DRAWING
845 SkAlphaType ImageProvider::AlphaTypeToSkAlphaType(const RefPtr<PixelMap>& pixmap)
846 {
847     switch (pixmap->GetAlphaType()) {
848         case AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
849             return SkAlphaType::kUnknown_SkAlphaType;
850         case AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
851             return SkAlphaType::kOpaque_SkAlphaType;
852         case AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
853             return SkAlphaType::kPremul_SkAlphaType;
854         case AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
855             return SkAlphaType::kUnpremul_SkAlphaType;
856         default:
857             return SkAlphaType::kUnknown_SkAlphaType;
858     }
859 }
860 #else
861 RSAlphaType ImageProvider::AlphaTypeToDrawingAlphaType(const RefPtr<PixelMap>& pixmap)
862 {
863     switch (pixmap->GetAlphaType()) {
864         case AlphaType::IMAGE_ALPHA_TYPE_UNKNOWN:
865             return RSAlphaType::ALPHATYPE_UNKNOWN;
866         case AlphaType::IMAGE_ALPHA_TYPE_OPAQUE:
867             return RSAlphaType::ALPHATYPE_OPAQUE;
868         case AlphaType::IMAGE_ALPHA_TYPE_PREMUL:
869             return RSAlphaType::ALPHATYPE_PREMUL;
870         case AlphaType::IMAGE_ALPHA_TYPE_UNPREMUL:
871             return RSAlphaType::ALPHATYPE_UNPREMUL;
872         default:
873             return RSAlphaType::ALPHATYPE_UNKNOWN;
874     }
875 }
876 #endif
877 
878 #ifndef USE_ROSEN_DRAWING
879 SkColorType ImageProvider::PixelFormatToSkColorType(const RefPtr<PixelMap>& pixmap)
880 {
881     switch (pixmap->GetPixelFormat()) {
882         case PixelFormat::RGB_565:
883             return SkColorType::kRGB_565_SkColorType;
884         case PixelFormat::RGBA_8888:
885             return SkColorType::kRGBA_8888_SkColorType;
886         case PixelFormat::BGRA_8888:
887             return SkColorType::kBGRA_8888_SkColorType;
888         case PixelFormat::ALPHA_8:
889             return SkColorType::kAlpha_8_SkColorType;
890         case PixelFormat::RGBA_F16:
891             return SkColorType::kRGBA_F16_SkColorType;
892         case PixelFormat::UNKNOWN:
893         case PixelFormat::ARGB_8888:
894         case PixelFormat::RGB_888:
895         case PixelFormat::NV21:
896         case PixelFormat::NV12:
897         case PixelFormat::CMYK:
898         default:
899             return SkColorType::kUnknown_SkColorType;
900     }
901 }
902 #else
903 RSColorType ImageProvider::PixelFormatToDrawingColorType(const RefPtr<PixelMap>& pixmap)
904 {
905     switch (pixmap->GetPixelFormat()) {
906         case PixelFormat::RGB_565:
907             return RSColorType::COLORTYPE_RGB_565;
908         case PixelFormat::RGBA_8888:
909             return RSColorType::COLORTYPE_RGBA_8888;
910         case PixelFormat::RGBA_1010102:
911             return RSColorType::COLORTYPE_RGBA_1010102;
912         case PixelFormat::BGRA_8888:
913             return RSColorType::COLORTYPE_BGRA_8888;
914         case PixelFormat::ALPHA_8:
915             return RSColorType::COLORTYPE_ALPHA_8;
916         case PixelFormat::RGBA_F16:
917             return RSColorType::COLORTYPE_RGBA_F16;
918         case PixelFormat::UNKNOWN:
919         case PixelFormat::ARGB_8888:
920         case PixelFormat::RGB_888:
921         case PixelFormat::NV21:
922         case PixelFormat::NV12:
923         case PixelFormat::CMYK:
924         default:
925             return RSColorType::COLORTYPE_UNKNOWN;
926     }
927 }
928 #endif
929 
930 } // namespace OHOS::Ace
931