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