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