• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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