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