1 /*
2 * Copyright (c) 2023-2024 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 "image_decoder.h"
17
18 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
19 #include "drawing/engine_adapter/skia_adapter/skia_image_info.h"
20 #include "include/codec/SkCodec.h"
21 #include "include/core/SkGraphics.h"
22
23 #include "base/image/image_source.h"
24 #include "base/image/pixel_map.h"
25 #include "base/log/ace_trace.h"
26 #include "core/components_ng/image_provider/drawing_image_data.h"
27 #include "core/components_ng/image_provider/image_utils.h"
28 #include "core/components_ng/render/adapter/pixelmap_image.h"
29 #include "core/components_ng/render/adapter/drawing_image.h"
30 #include "core/image/image_compressor.h"
31 #include "core/image/image_loader.h"
32
33 namespace OHOS::Ace::NG {
34 std::shared_mutex ImageDecoder::pixelMapMtx_;
35 std::unordered_map<std::string, WeakPtr<PixelMap>> ImageDecoder::weakPixelMapCache_;
36
GetFromPixelMapCache(const ImageSourceInfo & imageSourceInfo,const SizeF & size)37 WeakPtr<PixelMap> ImageDecoder::GetFromPixelMapCache(const ImageSourceInfo& imageSourceInfo, const SizeF& size)
38 {
39 if (!imageSourceInfo.IsSurportCachePixelmap()) {
40 return nullptr;
41 }
42 std::shared_lock<std::shared_mutex> lock(pixelMapMtx_);
43 auto key = ImageUtils::GenerateImageKey(imageSourceInfo, size);
44 // key exists -> task is running
45 auto it = weakPixelMapCache_.find(key);
46 if (it != weakPixelMapCache_.end()) {
47 return it->second;
48 }
49 return nullptr;
50 }
51
RemoveFromPixelMapCache(const ImageSourceInfo & imageSourceInfo,const SizeF & size)52 void ImageDecoder::RemoveFromPixelMapCache(const ImageSourceInfo& imageSourceInfo, const SizeF& size)
53 {
54 std::unique_lock<std::shared_mutex> lock(pixelMapMtx_);
55 auto key = ImageUtils::GenerateImageKey(imageSourceInfo, size);
56 weakPixelMapCache_.erase(key);
57 }
58
ClearPixelMapCache()59 void ImageDecoder::ClearPixelMapCache()
60 {
61 std::unique_lock<std::shared_mutex> lock(pixelMapMtx_);
62 weakPixelMapCache_.clear();
63 }
64
AddToPixelMapCache(const ImageSourceInfo & imageSourceInfo,const SizeF & size,WeakPtr<PixelMap> weakPixelMap)65 void ImageDecoder::AddToPixelMapCache(
66 const ImageSourceInfo& imageSourceInfo, const SizeF& size, WeakPtr<PixelMap> weakPixelMap)
67 {
68 if (!imageSourceInfo.IsSurportCachePixelmap()) {
69 return;
70 }
71 std::unique_lock<std::shared_mutex> lock(pixelMapMtx_);
72 auto key = ImageUtils::GenerateImageKey(imageSourceInfo, size);
73 weakPixelMapCache_.emplace(key, weakPixelMap);
74 }
75
MakeDrawingImage(const RefPtr<ImageObject> & obj,const ImageDecoderConfig & imageDecoderConfig)76 RefPtr<CanvasImage> ImageDecoder::MakeDrawingImage(
77 const RefPtr<ImageObject>& obj, const ImageDecoderConfig& imageDecoderConfig)
78 {
79 CHECK_NULL_RETURN(obj, nullptr);
80 ImageProvider::PrepareImageData(obj);
81 auto imageData = AceType::DynamicCast<DrawingImageData>(obj->GetData());
82 CHECK_NULL_RETURN(imageData, nullptr);
83 auto data = imageData->GetRSData();
84 CHECK_NULL_RETURN(data, nullptr);
85 ACE_SCOPED_TRACE("MakeSkiaImage %s", obj->GetSourceInfo().ToString().c_str());
86 // check compressed image cache
87 {
88 auto image = QueryCompressedCache(obj, data, imageDecoderConfig);
89 if (image) {
90 return image;
91 }
92 }
93
94 auto image = ResizeDrawingImage(obj, data, imageDecoderConfig);
95 CHECK_NULL_RETURN(image, nullptr);
96 auto canvasImage = CanvasImage::Create(&image);
97
98 if (ImageCompressor::GetInstance()->CanCompress()) {
99 TryCompress(obj, DynamicCast<DrawingImage>(canvasImage), imageDecoderConfig);
100 }
101 return canvasImage;
102 }
103
CreatePixelMapImage(const ImageDfxConfig & imageDfxConfig,const RefPtr<PixelMap> & pixmap,const ImageDecoderConfig & imageDecoderConfig)104 RefPtr<CanvasImage> CreatePixelMapImage(
105 const ImageDfxConfig& imageDfxConfig, const RefPtr<PixelMap>& pixmap, const ImageDecoderConfig& imageDecoderConfig)
106 {
107 auto image = PixelMapImage::Create(pixmap);
108 if (SystemProperties::GetDebugPixelMapSaveEnabled()) {
109 TAG_LOGI(AceLogTag::ACE_IMAGE,
110 "Image Decode success, Info:%{public}s-%{public}s-%{public}d x %{public}d-%{public}d-%{public}d",
111 imageDfxConfig.ToStringWithSrc().c_str(), imageDecoderConfig.desiredSize_.ToString().c_str(),
112 image->GetWidth(), image->GetHeight(), imageDecoderConfig.isHdrDecoderNeed_,
113 static_cast<int32_t>(imageDecoderConfig.photoDecodeFormat_));
114 pixmap->SavePixelMapToFile(imageDfxConfig.ToStringWithoutSrc() + "_decode_");
115 }
116 return image;
117 }
118
MakePixmapImage(const RefPtr<ImageObject> & obj,const ImageDecoderConfig & imageDecoderConfig,ImageErrorInfo & errorInfo)119 RefPtr<CanvasImage> ImageDecoder::MakePixmapImage(
120 const RefPtr<ImageObject>& obj, const ImageDecoderConfig& imageDecoderConfig, ImageErrorInfo& errorInfo)
121 {
122 CHECK_NULL_RETURN(obj, nullptr);
123 ImageProvider::PrepareImageData(obj);
124 auto imageData = AceType::DynamicCast<DrawingImageData>(obj->GetData());
125 CHECK_NULL_RETURN(imageData, nullptr);
126 auto data = imageData->GetRSData();
127 CHECK_NULL_RETURN(data, nullptr);
128 auto imageDfxConfig = obj->GetImageDfxConfig();
129 auto src = imageDfxConfig.GetImageSrc();
130 uint32_t mediaErrorCode = 0;
131 auto source = ImageSource::Create(static_cast<const uint8_t*>(data->GetData()), data->GetSize(), mediaErrorCode);
132 if (!source) {
133 TAG_LOGE(AceLogTag::ACE_IMAGE, "ImageSouce Create Fail, %{private}s-%{public}s.", src.c_str(),
134 imageDfxConfig.ToStringWithoutSrc().c_str());
135 errorInfo = { ImageErrorCode::MAKE_CANVAS_IMAGE_SOURCE_CREATE_FAILED,
136 "ErrorCode: " + std::to_string(mediaErrorCode) + ", image source create failed." };
137 return nullptr;
138 }
139
140 auto width = static_cast<int32_t>(std::lround(imageDecoderConfig.desiredSize_.Width()));
141 auto height = static_cast<int32_t>(std::lround(imageDecoderConfig.desiredSize_.Height()));
142 std::pair<int32_t, int32_t> sourceSize = source->GetImageSize();
143 // Determine whether to decode the width and height of each other based on the orientation
144 SwapDecodeSize(obj, width, height);
145 std::string isTrimMemRebuild = "False";
146 if (imageDfxConfig.GetIsTrimMemRecycle()) {
147 isTrimMemRebuild = "True";
148 TAG_LOGI(AceLogTag::ACE_IMAGE, "CreateImagePixelMapRebuild, %{private}s-%{public}s.",
149 src.c_str(), imageDfxConfig.ToStringWithoutSrc().c_str());
150 }
151 ACE_SCOPED_TRACE("CreateImagePixelMap %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ],"
152 "[%d-%d-%d], isTrimMemRebuild: [%s]",
153 src.c_str(), sourceSize.first, sourceSize.second, width, height,
154 static_cast<int32_t>(imageDecoderConfig.isHdrDecoderNeed_),
155 static_cast<int32_t>(imageDecoderConfig.imageQuality_),
156 static_cast<int32_t>(imageDecoderConfig.photoDecodeFormat_), isTrimMemRebuild.c_str());
157
158 PixelMapConfig pixelMapConfig = { imageDecoderConfig.imageQuality_, imageDecoderConfig.isHdrDecoderNeed_,
159 imageDecoderConfig.photoDecodeFormat_ };
160 auto pixmap = source->CreatePixelMap({ width, height }, mediaErrorCode, pixelMapConfig);
161 if (!pixmap) {
162 TAG_LOGE(AceLogTag::ACE_IMAGE, "PixelMap Create Fail, %{private}s-%{public}s.", src.c_str(),
163 imageDfxConfig.ToStringWithoutSrc().c_str());
164 errorInfo = { ImageErrorCode::MAKE_CANVAS_IMAGE_PIXELMAP_FAILED,
165 "ErrorCode: " + std::to_string(mediaErrorCode) + ", pixelmap create failed." };
166 return nullptr;
167 }
168
169 auto image = CreatePixelMapImage(imageDfxConfig, pixmap, imageDecoderConfig);
170
171 AddToPixelMapCache(obj->GetSourceInfo(), imageDecoderConfig.desiredSize_, image->GetPixelMap());
172
173 return image;
174 }
175
SwapDecodeSize(const RefPtr<ImageObject> & obj,int32_t & width,int32_t & height)176 void ImageDecoder::SwapDecodeSize(const RefPtr<ImageObject>& obj, int32_t& width, int32_t& height)
177 {
178 if (width == 0 || height == 0 || obj->GetUserOrientation() == ImageRotateOrientation::UP) {
179 return;
180 }
181 auto orientation = obj->GetOrientation();
182 if (orientation == ImageRotateOrientation::LEFT || orientation == ImageRotateOrientation::RIGHT ||
183 orientation == ImageRotateOrientation::LEFT_MIRRORED || orientation == ImageRotateOrientation::RIGHT_MIRRORED) {
184 std::swap(width, height);
185 }
186 }
187
ForceResizeImage(const std::shared_ptr<RSImage> & image,const RSImageInfo & info)188 std::shared_ptr<RSImage> ImageDecoder::ForceResizeImage(const std::shared_ptr<RSImage>& image, const RSImageInfo& info)
189 {
190 ACE_FUNCTION_TRACE();
191 RSBitmap bitmap;
192 bitmap.Build(info);
193
194 auto res = image->ScalePixels(bitmap, RSSamplingOptions(RSFilterMode::LINEAR, RSMipmapMode::NONE), false);
195
196 CHECK_NULL_RETURN(res, image);
197
198 bitmap.SetImmutable();
199 auto drImage = std::make_shared<RSImage>();
200 drImage->BuildFromBitmap(bitmap);
201 return drImage;
202 }
203
ResizeDrawingImage(const RefPtr<ImageObject> & obj,std::shared_ptr<RSData> data,const ImageDecoderConfig & imageDecoderConfig)204 std::shared_ptr<RSImage> ImageDecoder::ResizeDrawingImage(
205 const RefPtr<ImageObject>& obj, std::shared_ptr<RSData> data, const ImageDecoderConfig& imageDecoderConfig)
206 {
207 CHECK_NULL_RETURN(data, nullptr);
208 RSDataWrapper* wrapper = new RSDataWrapper{data};
209 auto skData =
210 SkData::MakeWithProc(data->GetData(), data->GetSize(), RSDataWrapperReleaseProc, wrapper);
211 auto encodedImage = std::make_shared<RSImage>();
212 if (!encodedImage->MakeFromEncoded(data)) {
213 return nullptr;
214 }
215 CHECK_NULL_RETURN(imageDecoderConfig.desiredSize_.IsPositive(), encodedImage);
216
217 auto width = std::lround(imageDecoderConfig.desiredSize_.Width());
218 auto height = std::lround(imageDecoderConfig.desiredSize_.Height());
219
220 auto codec = SkCodec::MakeFromData(skData);
221 CHECK_NULL_RETURN(codec, {});
222 auto info = codec->getInfo();
223
224 ACE_SCOPED_TRACE("ImageResize %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ]",
225 obj->GetSourceInfo().ToString().c_str(), info.width(), info.height(), static_cast<int32_t>(width),
226 static_cast<int32_t>(height));
227
228 // sourceSize is set by developer, then we will force scaling to [TargetSize] using SkImage::scalePixels,
229 // this method would succeed even if the codec doesn't support that size.
230 if (imageDecoderConfig.forceResize_) {
231 info = info.makeWH(width, height);
232 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
233 return ForceResizeImage(encodedImage, imageInfo);
234 }
235
236 if ((info.width() > width && info.height() > height)) {
237 // If the image is larger than the target size, we will scale it down to the target size.
238 // DesiredSize might not be compatible with the codec, so we find the closest size supported by the codec
239 auto scale = std::max(static_cast<float>(width) / info.width(), static_cast<float>(height) / info.height());
240 auto idealSize = codec->getScaledDimensions(scale);
241 if (SystemProperties::GetDebugEnabled()) {
242 TAG_LOGD(AceLogTag::ACE_IMAGE, "desiredSize = %{public}s, codec idealSize: %{public}dx%{public}d",
243 imageDecoderConfig.desiredSize_.ToString().c_str(), idealSize.width(), idealSize.height());
244 }
245
246 info = info.makeWH(idealSize.width(), idealSize.height());
247 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
248 RSBitmap bitmap;
249 bitmap.Build(imageInfo);
250 auto res = codec->getPixels(info, bitmap.GetPixels(), bitmap.GetRowBytes());
251 CHECK_NULL_RETURN(res == SkCodec::kSuccess, encodedImage);
252 auto image = std::make_shared<RSImage>();
253 image->BuildFromBitmap(bitmap);
254 return image;
255 }
256 return encodedImage;
257 }
258
QueryCompressedCache(const RefPtr<ImageObject> & obj,std::shared_ptr<RSData> data,const ImageDecoderConfig & imageDecoderConfig)259 RefPtr<CanvasImage> ImageDecoder::QueryCompressedCache(
260 const RefPtr<ImageObject>& obj, std::shared_ptr<RSData> data, const ImageDecoderConfig& imageDecoderConfig)
261 {
262 auto key = ImageUtils::GenerateImageKey(obj->GetSourceInfo(), imageDecoderConfig.desiredSize_);
263 auto cachedData = ImageLoader::LoadImageDataFromFileCache(key, ".astc");
264 CHECK_NULL_RETURN(cachedData, {});
265
266 auto rosenImageData = AceType::DynamicCast<DrawingImageData>(cachedData);
267 CHECK_NULL_RETURN(rosenImageData, {});
268 auto stripped = ImageCompressor::StripFileHeader(rosenImageData->GetRSData());
269 TAG_LOGI(AceLogTag::ACE_IMAGE, "use astc cache %{private}s", key.c_str());
270
271 // create encoded SkImage to use its uniqueId
272 CHECK_NULL_RETURN(data, {});
273 auto image = std::make_shared<RSImage>();
274 if (!image->MakeFromEncoded(data)) {
275 return nullptr;
276 }
277 auto canvasImage = AceType::DynamicCast<DrawingImage>(CanvasImage::Create(&image));
278 // round width and height to nearest int
279 int32_t dstWidth = std::lround(imageDecoderConfig.desiredSize_.Width());
280 int32_t dstHeight = std::lround(imageDecoderConfig.desiredSize_.Height());
281 canvasImage->SetCompressData(stripped, dstWidth, dstHeight);
282 canvasImage->ReplaceRSImage(nullptr);
283 return canvasImage;
284 }
285
TryCompress(const RefPtr<ImageObject> & obj,const RefPtr<DrawingImage> & image,const ImageDecoderConfig & imageDecoderConfig)286 void ImageDecoder::TryCompress(
287 const RefPtr<ImageObject>& obj, const RefPtr<DrawingImage>& image, const ImageDecoderConfig& imageDecoderConfig)
288 {
289 #ifdef UPLOAD_GPU_DISABLED
290 // If want to dump draw command or gpu disabled, should use CPU image.
291 return;
292 #else
293 // decode image to texture if not decoded
294 auto rsImage = image->GetImage();
295 CHECK_NULL_VOID(rsImage);
296 RSBitmapFormat rsBitmapFormat { rsImage->GetColorType(), rsImage->GetAlphaType() };
297 RSBitmap rsBitmap;
298 if (!rsBitmap.Build(rsImage->GetWidth(), rsImage->GetHeight(), rsBitmapFormat)) {
299 TAG_LOGW(AceLogTag::ACE_IMAGE, "rsBitmap build fail.");
300 return;
301 }
302 CHECK_NULL_VOID(rsImage->ReadPixels(rsBitmap, 0, 0));
303 auto width = rsBitmap.GetWidth();
304 auto height = rsBitmap.GetHeight();
305 // try compress image
306 if (ImageCompressor::GetInstance()->CanCompress()) {
307 auto key = ImageUtils::GenerateImageKey(obj->GetSourceInfo(), imageDecoderConfig.desiredSize_);
308 auto compressData = ImageCompressor::GetInstance()->GpuCompress(key, rsBitmap, width, height);
309 ImageCompressor::GetInstance()->WriteToFile(key, compressData, { width, height });
310 if (compressData) {
311 // replace rsImage of [CanvasImage] with [rasterizedImage]
312 image->SetCompressData(compressData, width, height);
313 image->ReplaceRSImage(nullptr);
314 } else {
315 auto rasterizedImage = std::make_shared<RSImage>();
316 rasterizedImage->BuildFromBitmap(rsBitmap);
317 image->ReplaceRSImage(rasterizedImage);
318 }
319 auto taskExecutor = Container::CurrentTaskExecutor();
320 auto releaseTask = ImageCompressor::GetInstance()->ScheduleReleaseTask();
321 if (taskExecutor) {
322 taskExecutor->PostDelayedTask(releaseTask, TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs,
323 "ArkUIImageCompressorScheduleRelease");
324 } else {
325 ImageUtils::PostToBg(std::move(releaseTask), "ArkUIImageCompressorScheduleRelease");
326 }
327 }
328 SkGraphics::PurgeResourceCache();
329 #endif
330 }
331 } // namespace OHOS::Ace::NG
332