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