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