1 /*
2 * Copyright (c) 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 "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 = std::lround(desiredSize_.Width());
83 auto height = 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 ACE_SCOPED_TRACE("CreateImagePixelMap %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ]", srcStr.c_str(),
88 sourceSize.first, sourceSize.second, static_cast<int32_t>(width), static_cast<int32_t>(height));
89 auto pixmap = source->CreatePixelMap({ width, height }, imageQuality, isHdrDecoderNeed);
90
91 CHECK_NULL_RETURN(pixmap, nullptr);
92 auto image = PixelMapImage::Create(pixmap);
93
94 if (SystemProperties::GetDebugEnabled()) {
95 TAG_LOGD(AceLogTag::ACE_IMAGE,
96 "decode to pixmap, src=%{private}s, resolutionQuality = %{public}s, desiredSize = %{public}s, pixmap size "
97 "= "
98 "%{public}d x %{public}d",
99 obj_->GetSourceInfo().ToString().c_str(),
100 GetResolutionQuality(imageQuality).c_str(),
101 desiredSize_.ToString().c_str(),
102 image->GetWidth(),
103 image->GetHeight());
104 }
105
106 return image;
107 }
108
ForceResizeImage(const std::shared_ptr<RSImage> & image,const RSImageInfo & info)109 std::shared_ptr<RSImage> ImageDecoder::ForceResizeImage(const std::shared_ptr<RSImage>& image, const RSImageInfo& info)
110 {
111 ACE_FUNCTION_TRACE();
112 RSBitmap bitmap;
113 bitmap.Build(info);
114
115 auto res = image->ScalePixels(bitmap, RSSamplingOptions(RSFilterMode::LINEAR, RSMipmapMode::NONE), false);
116
117 CHECK_NULL_RETURN(res, image);
118
119 bitmap.SetImmutable();
120 auto drImage = std::make_shared<RSImage>();
121 drImage->BuildFromBitmap(bitmap);
122 return drImage;
123 }
124
ResizeDrawingImage()125 std::shared_ptr<RSImage> ImageDecoder::ResizeDrawingImage()
126 {
127 CHECK_NULL_RETURN(data_, nullptr);
128 auto rsSkiaData = data_->GetImpl<Rosen::Drawing::SkiaData>();
129 CHECK_NULL_RETURN(rsSkiaData, nullptr);
130 auto skData = rsSkiaData->GetSkData();
131 auto encodedImage = std::make_shared<RSImage>();
132 if (!encodedImage->MakeFromEncoded(data_)) {
133 return nullptr;
134 }
135 CHECK_NULL_RETURN(desiredSize_.IsPositive(), encodedImage);
136
137 auto width = std::lround(desiredSize_.Width());
138 auto height = std::lround(desiredSize_.Height());
139
140 auto codec = SkCodec::MakeFromData(skData);
141 CHECK_NULL_RETURN(codec, {});
142 auto info = codec->getInfo();
143
144 ACE_SCOPED_TRACE("ImageResize %s, sourceSize: [ %d, %d ], targetSize: [ %d, %d ]",
145 obj_->GetSourceInfo().ToString().c_str(), info.width(), info.height(), static_cast<int32_t>(width),
146 static_cast<int32_t>(height));
147
148 // sourceSize is set by developer, then we will force scaling to [TargetSize] using SkImage::scalePixels,
149 // this method would succeed even if the codec doesn't support that size.
150 if (forceResize_) {
151 info = info.makeWH(width, height);
152 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
153 return ForceResizeImage(encodedImage, imageInfo);
154 }
155
156 if ((info.width() > width && info.height() > height)) {
157 // If the image is larger than the target size, we will scale it down to the target size.
158 // DesiredSize might not be compatible with the codec, so we find the closest size supported by the codec
159 auto scale = std::max(static_cast<float>(width) / info.width(), static_cast<float>(height) / info.height());
160 auto idealSize = codec->getScaledDimensions(scale);
161 if (SystemProperties::GetDebugEnabled()) {
162 TAG_LOGD(AceLogTag::ACE_IMAGE, "desiredSize = %{public}s, codec idealSize: %{public}dx%{public}d",
163 desiredSize_.ToString().c_str(), idealSize.width(), idealSize.height());
164 }
165
166 info = info.makeWH(idealSize.width(), idealSize.height());
167 auto imageInfo = Rosen::Drawing::SkiaImageInfo::ConvertToRSImageInfo(info);
168 RSBitmap bitmap;
169 bitmap.Build(imageInfo);
170 auto res = codec->getPixels(info, bitmap.GetPixels(), bitmap.GetRowBytes());
171 CHECK_NULL_RETURN(res == SkCodec::kSuccess, encodedImage);
172 auto image = std::make_shared<RSImage>();
173 image->BuildFromBitmap(bitmap);
174 return image;
175 }
176 return encodedImage;
177 }
178
QueryCompressedCache()179 RefPtr<CanvasImage> ImageDecoder::QueryCompressedCache()
180 {
181 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
182 auto cachedData = ImageLoader::LoadImageDataFromFileCache(key, ".astc");
183 CHECK_NULL_RETURN(cachedData, {});
184
185 auto rosenImageData = AceType::DynamicCast<DrawingImageData>(cachedData);
186 CHECK_NULL_RETURN(rosenImageData, {});
187 auto stripped = ImageCompressor::StripFileHeader(rosenImageData->GetRSData());
188 TAG_LOGI(AceLogTag::ACE_IMAGE, "use astc cache %{public}s", key.c_str());
189
190 // create encoded SkImage to use its uniqueId
191 CHECK_NULL_RETURN(data_, {});
192 auto image = std::make_shared<RSImage>();
193 if (!image->MakeFromEncoded(data_)) {
194 return nullptr;
195 }
196 auto canvasImage = AceType::DynamicCast<DrawingImage>(CanvasImage::Create(&image));
197 // round width and height to nearest int
198 int32_t dstWidth = std::lround(desiredSize_.Width());
199 int32_t dstHeight = std::lround(desiredSize_.Height());
200 canvasImage->SetCompressData(stripped, dstWidth, dstHeight);
201 canvasImage->ReplaceRSImage(nullptr);
202 return canvasImage;
203 }
204
TryCompress(const RefPtr<DrawingImage> & image)205 void ImageDecoder::TryCompress(const RefPtr<DrawingImage>& image)
206 {
207 #ifdef UPLOAD_GPU_DISABLED
208 // If want to dump draw command or gpu disabled, should use CPU image.
209 return;
210 #else
211 // decode image to texture if not decoded
212 auto rsImage = image->GetImage();
213 CHECK_NULL_VOID(rsImage);
214 RSBitmapFormat rsBitmapFormat { rsImage->GetColorType(), rsImage->GetAlphaType() };
215 RSBitmap rsBitmap;
216 if (!rsBitmap.Build(rsImage->GetWidth(), rsImage->GetHeight(), rsBitmapFormat)) {
217 TAG_LOGW(AceLogTag::ACE_IMAGE, "rsBitmap build fail.");
218 return;
219 }
220 CHECK_NULL_VOID(rsImage->ReadPixels(rsBitmap, 0, 0));
221 auto width = rsBitmap.GetWidth();
222 auto height = rsBitmap.GetHeight();
223 // try compress image
224 if (ImageCompressor::GetInstance()->CanCompress()) {
225 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
226 auto compressData = ImageCompressor::GetInstance()->GpuCompress(key, rsBitmap, width, height);
227 ImageCompressor::GetInstance()->WriteToFile(key, compressData, { width, height });
228 if (compressData) {
229 // replace rsImage of [CanvasImage] with [rasterizedImage]
230 image->SetCompressData(compressData, width, height);
231 image->ReplaceRSImage(nullptr);
232 } else {
233 auto rasterizedImage = std::make_shared<RSImage>();
234 rasterizedImage->BuildFromBitmap(rsBitmap);
235 image->ReplaceRSImage(rasterizedImage);
236 }
237 auto taskExecutor = Container::CurrentTaskExecutor();
238 auto releaseTask = ImageCompressor::GetInstance()->ScheduleReleaseTask();
239 if (taskExecutor) {
240 taskExecutor->PostDelayedTask(releaseTask, TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs,
241 "ArkUIImageCompressorScheduleRelease");
242 } else {
243 ImageUtils::PostToBg(std::move(releaseTask), "ArkUIImageCompressorScheduleRelease");
244 }
245 }
246 SkGraphics::PurgeResourceCache();
247 #endif
248 }
249
GetResolutionQuality(AIImageQuality imageQuality)250 std::string ImageDecoder::GetResolutionQuality(AIImageQuality imageQuality)
251 {
252 switch (imageQuality) {
253 case AIImageQuality::NONE:
254 return "NONE";
255 case AIImageQuality::LOW:
256 return "LOW";
257 case AIImageQuality::NORMAL:
258 return "MEDIUM";
259 case AIImageQuality::HIGH:
260 return "HIGH";
261 default:
262 return "LOW";
263 }
264 }
265 } // namespace OHOS::Ace::NG
266