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 <mutex>
19 #include <utility>
20
21 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
22 #include "drawing/engine_adapter/skia_adapter/skia_image_info.h"
23 #include "include/codec/SkCodec.h"
24 #include "include/core/SkBitmap.h"
25 #include "include/core/SkGraphics.h"
26
27 #include "base/image/image_source.h"
28 #include "base/log/ace_trace.h"
29 #include "base/memory/referenced.h"
30 #include "base/utils/utils.h"
31 #include "core/common/container.h"
32 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
33 #include "core/components_ng/image_provider/image_object.h"
34 #include "core/components_ng/image_provider/image_provider.h"
35 #include "core/components_ng/image_provider/image_utils.h"
36 #include "core/components_ng/render/adapter/pixelmap_image.h"
37 #include "core/components_ng/render/adapter/rosen/drawing_image.h"
38 #include "core/components_ng/render/canvas_image.h"
39 #include "core/image/image_compressor.h"
40 #include "core/image/image_loader.h"
41
42 namespace OHOS::Ace::NG {
ImageDecoder(const RefPtr<ImageObject> & obj,const SizeF & size,bool forceResize)43 ImageDecoder::ImageDecoder(const RefPtr<ImageObject>& obj, const SizeF& size, bool forceResize)
44 : obj_(obj), desiredSize_(size), forceResize_(forceResize)
45 {
46 CHECK_NULL_VOID(obj_);
47 CHECK_NULL_VOID(ImageProvider::PrepareImageData(obj_));
48
49 auto data = AceType::DynamicCast<DrawingImageData>(obj_->GetData());
50 CHECK_NULL_VOID(data);
51 data_ = data->GetRSData();
52 }
53
MakeDrawingImage()54 RefPtr<CanvasImage> ImageDecoder::MakeDrawingImage()
55 {
56 CHECK_NULL_RETURN(obj_ && data_, nullptr);
57 ACE_SCOPED_TRACE("MakeSkiaImage %s", obj_->GetSourceInfo().ToString().c_str());
58 // check compressed image cache
59 {
60 auto image = QueryCompressedCache();
61 if (image) {
62 return image;
63 }
64 }
65
66 auto image = ResizeDrawingImage();
67 CHECK_NULL_RETURN(image, nullptr);
68 auto canvasImage = CanvasImage::Create(&image);
69
70 if (ImageCompressor::GetInstance()->CanCompress()) {
71 TryCompress(DynamicCast<DrawingImage>(canvasImage));
72 }
73 return canvasImage;
74 }
75
MakePixmapImage(AIImageQuality imageQuality,bool isHdrDecoderNeed)76 RefPtr<CanvasImage> ImageDecoder::MakePixmapImage(AIImageQuality imageQuality, bool isHdrDecoderNeed)
77 {
78 CHECK_NULL_RETURN(obj_ && data_, nullptr);
79 auto source = ImageSource::Create(static_cast<const uint8_t*>(data_->GetData()), data_->GetSize());
80 CHECK_NULL_RETURN(source, nullptr);
81
82 auto width = static_cast<int32_t>(std::lround(desiredSize_.Width()));
83 auto height = static_cast<int32_t>(std::lround(desiredSize_.Height()));
84 std::pair<int32_t, int32_t> sourceSize = source->GetImageSize();
85 auto src = obj_->GetSourceInfo();
86 auto srcStr = src.GetSrcType() == SrcType::BASE64 ? src.GetKey() : src.ToString();
87 // Determine whether to decode the width and height of each other based on the orientation
88 SwapDecodeSize(width, height);
89 ACE_SCOPED_TRACE("CreateImagePixelMap %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ]", srcStr.c_str(),
90 sourceSize.first, sourceSize.second, static_cast<int32_t>(width), static_cast<int32_t>(height));
91
92 auto pixmap = source->CreatePixelMap({ width, height }, imageQuality, isHdrDecoderNeed);
93
94 CHECK_NULL_RETURN(pixmap, nullptr);
95 auto image = PixelMapImage::Create(pixmap);
96
97 if (SystemProperties::GetDebugEnabled()) {
98 TAG_LOGD(AceLogTag::ACE_IMAGE,
99 "decode to pixmap, src=%{private}s, resolutionQuality = %{public}s, desiredSize = %{public}s, pixmap size "
100 "= "
101 "%{public}d x %{public}d",
102 obj_->GetSourceInfo().ToString().c_str(),
103 GetResolutionQuality(imageQuality).c_str(),
104 desiredSize_.ToString().c_str(),
105 image->GetWidth(),
106 image->GetHeight());
107 }
108
109 return image;
110 }
111
SwapDecodeSize(int32_t & width,int32_t & height)112 void ImageDecoder::SwapDecodeSize(int32_t& width, int32_t& height)
113 {
114 if (width == 0 || height == 0 || obj_->GetUserOrientation() == ImageRotateOrientation::UP) {
115 return;
116 }
117 auto orientation = obj_->GetOrientation();
118 if (orientation == ImageRotateOrientation::LEFT || orientation == ImageRotateOrientation::RIGHT) {
119 std::swap(width, height);
120 }
121 }
122
ForceResizeImage(const std::shared_ptr<RSImage> & image,const RSImageInfo & info)123 std::shared_ptr<RSImage> ImageDecoder::ForceResizeImage(const std::shared_ptr<RSImage>& image, const RSImageInfo& info)
124 {
125 ACE_FUNCTION_TRACE();
126 RSBitmap bitmap;
127 bitmap.Build(info);
128
129 auto res = image->ScalePixels(bitmap, RSSamplingOptions(RSFilterMode::LINEAR, RSMipmapMode::NONE), false);
130
131 CHECK_NULL_RETURN(res, image);
132
133 bitmap.SetImmutable();
134 auto drImage = std::make_shared<RSImage>();
135 drImage->BuildFromBitmap(bitmap);
136 return drImage;
137 }
138
ResizeDrawingImage()139 std::shared_ptr<RSImage> ImageDecoder::ResizeDrawingImage()
140 {
141 CHECK_NULL_RETURN(data_, nullptr);
142 auto rsSkiaData = data_->GetImpl<Rosen::Drawing::SkiaData>();
143 CHECK_NULL_RETURN(rsSkiaData, nullptr);
144 auto skData = rsSkiaData->GetSkData();
145 auto encodedImage = std::make_shared<RSImage>();
146 if (!encodedImage->MakeFromEncoded(data_)) {
147 return nullptr;
148 }
149 CHECK_NULL_RETURN(desiredSize_.IsPositive(), encodedImage);
150
151 auto width = std::lround(desiredSize_.Width());
152 auto height = std::lround(desiredSize_.Height());
153
154 auto codec = SkCodec::MakeFromData(skData);
155 CHECK_NULL_RETURN(codec, {});
156 auto info = codec->getInfo();
157
158 ACE_SCOPED_TRACE("ImageResize %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ]",
159 obj_->GetSourceInfo().ToString().c_str(), info.width(), info.height(), static_cast<int32_t>(width),
160 static_cast<int32_t>(height));
161
162 // sourceSize is set by developer, then we will force scaling to [TargetSize] using SkImage::scalePixels,
163 // this method would succeed even if the codec doesn't support that size.
164 if (forceResize_) {
165 info = info.makeWH(width, height);
166 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
167 return ForceResizeImage(encodedImage, imageInfo);
168 }
169
170 if ((info.width() > width && info.height() > height)) {
171 // If the image is larger than the target size, we will scale it down to the target size.
172 // DesiredSize might not be compatible with the codec, so we find the closest size supported by the codec
173 auto scale = std::max(static_cast<float>(width) / info.width(), static_cast<float>(height) / info.height());
174 auto idealSize = codec->getScaledDimensions(scale);
175 if (SystemProperties::GetDebugEnabled()) {
176 TAG_LOGD(AceLogTag::ACE_IMAGE, "desiredSize = %{public}s, codec idealSize: %{public}dx%{public}d",
177 desiredSize_.ToString().c_str(), idealSize.width(), idealSize.height());
178 }
179
180 info = info.makeWH(idealSize.width(), idealSize.height());
181 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
182 RSBitmap bitmap;
183 bitmap.Build(imageInfo);
184 auto res = codec->getPixels(info, bitmap.GetPixels(), bitmap.GetRowBytes());
185 CHECK_NULL_RETURN(res == SkCodec::kSuccess, encodedImage);
186 auto image = std::make_shared<RSImage>();
187 image->BuildFromBitmap(bitmap);
188 return image;
189 }
190 return encodedImage;
191 }
192
QueryCompressedCache()193 RefPtr<CanvasImage> ImageDecoder::QueryCompressedCache()
194 {
195 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
196 auto cachedData = ImageLoader::LoadImageDataFromFileCache(key, ".astc");
197 CHECK_NULL_RETURN(cachedData, {});
198
199 auto rosenImageData = AceType::DynamicCast<DrawingImageData>(cachedData);
200 CHECK_NULL_RETURN(rosenImageData, {});
201 auto stripped = ImageCompressor::StripFileHeader(rosenImageData->GetRSData());
202 TAG_LOGI(AceLogTag::ACE_IMAGE, "use astc cache %{public}s", key.c_str());
203
204 // create encoded SkImage to use its uniqueId
205 CHECK_NULL_RETURN(data_, {});
206 auto image = std::make_shared<RSImage>();
207 if (!image->MakeFromEncoded(data_)) {
208 return nullptr;
209 }
210 auto canvasImage = AceType::DynamicCast<DrawingImage>(CanvasImage::Create(&image));
211 // round width and height to nearest int
212 int32_t dstWidth = std::lround(desiredSize_.Width());
213 int32_t dstHeight = std::lround(desiredSize_.Height());
214 canvasImage->SetCompressData(stripped, dstWidth, dstHeight);
215 canvasImage->ReplaceRSImage(nullptr);
216 return canvasImage;
217 }
218
TryCompress(const RefPtr<DrawingImage> & image)219 void ImageDecoder::TryCompress(const RefPtr<DrawingImage>& image)
220 {
221 #ifdef UPLOAD_GPU_DISABLED
222 // If want to dump draw command or gpu disabled, should use CPU image.
223 return;
224 #else
225 // decode image to texture if not decoded
226 auto rsImage = image->GetImage();
227 CHECK_NULL_VOID(rsImage);
228 RSBitmapFormat rsBitmapFormat { rsImage->GetColorType(), rsImage->GetAlphaType() };
229 RSBitmap rsBitmap;
230 if (!rsBitmap.Build(rsImage->GetWidth(), rsImage->GetHeight(), rsBitmapFormat)) {
231 TAG_LOGW(AceLogTag::ACE_IMAGE, "rsBitmap build fail.");
232 return;
233 }
234 CHECK_NULL_VOID(rsImage->ReadPixels(rsBitmap, 0, 0));
235 auto width = rsBitmap.GetWidth();
236 auto height = rsBitmap.GetHeight();
237 // try compress image
238 if (ImageCompressor::GetInstance()->CanCompress()) {
239 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
240 auto compressData = ImageCompressor::GetInstance()->GpuCompress(key, rsBitmap, width, height);
241 ImageCompressor::GetInstance()->WriteToFile(key, compressData, { width, height });
242 if (compressData) {
243 // replace rsImage of [CanvasImage] with [rasterizedImage]
244 image->SetCompressData(compressData, width, height);
245 image->ReplaceRSImage(nullptr);
246 } else {
247 auto rasterizedImage = std::make_shared<RSImage>();
248 rasterizedImage->BuildFromBitmap(rsBitmap);
249 image->ReplaceRSImage(rasterizedImage);
250 }
251 auto taskExecutor = Container::CurrentTaskExecutor();
252 auto releaseTask = ImageCompressor::GetInstance()->ScheduleReleaseTask();
253 if (taskExecutor) {
254 taskExecutor->PostDelayedTask(releaseTask, TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs,
255 "ArkUIImageCompressorScheduleRelease");
256 } else {
257 ImageUtils::PostToBg(std::move(releaseTask), "ArkUIImageCompressorScheduleRelease");
258 }
259 }
260 SkGraphics::PurgeResourceCache();
261 #endif
262 }
263
GetResolutionQuality(AIImageQuality imageQuality)264 std::string ImageDecoder::GetResolutionQuality(AIImageQuality imageQuality)
265 {
266 switch (imageQuality) {
267 case AIImageQuality::NONE:
268 return "NONE";
269 case AIImageQuality::LOW:
270 return "LOW";
271 case AIImageQuality::NORMAL:
272 return "MEDIUM";
273 case AIImageQuality::HIGH:
274 return "HIGH";
275 default:
276 return "LOW";
277 }
278 }
279 } // namespace OHOS::Ace::NG
280