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 "include/codec/SkCodec.h"
22 #include "include/core/SkBitmap.h"
23 #include "include/core/SkGraphics.h"
24 #ifdef USE_ROSEN_DRAWING
25 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
26 #endif
27
28 #include "base/image/image_source.h"
29 #include "base/log/ace_trace.h"
30 #include "base/memory/referenced.h"
31 #include "base/utils/utils.h"
32 #include "core/common/container.h"
33 #ifndef USE_ROSEN_DRAWING
34 #include "core/components_ng/image_provider/adapter/skia_image_data.h"
35 #else
36 #include "core/components_ng/image_provider/adapter/rosen/drawing_image_data.h"
37 #endif
38 #include "core/components_ng/image_provider/image_object.h"
39 #include "core/components_ng/image_provider/image_provider.h"
40 #include "core/components_ng/image_provider/image_utils.h"
41 #include "core/components_ng/render/adapter/pixelmap_image.h"
42 #ifndef USE_ROSEN_DRAWING
43 #include "core/components_ng/render/adapter/skia_image.h"
44 #else
45 #include "core/components_ng/render/adapter/rosen/drawing_image.h"
46 #endif
47 #include "core/components_ng/render/canvas_image.h"
48 #include "core/image/image_compressor.h"
49 #include "core/image/image_loader.h"
50
51 namespace OHOS::Ace::NG {
ImageDecoder(const RefPtr<ImageObject> & obj,const SizeF & size,bool forceResize)52 ImageDecoder::ImageDecoder(const RefPtr<ImageObject>& obj, const SizeF& size, bool forceResize)
53 : obj_(obj), desiredSize_(size), forceResize_(forceResize)
54 {
55 CHECK_NULL_VOID(obj_);
56 CHECK_NULL_VOID(ImageProvider::PrepareImageData(obj_));
57
58 #ifndef USE_ROSEN_DRAWING
59 auto data = AceType::DynamicCast<SkiaImageData>(obj_->GetData());
60 CHECK_NULL_VOID(data);
61 data_ = data->GetSkData();
62 #else
63 auto data = AceType::DynamicCast<DrawingImageData>(obj_->GetData());
64 CHECK_NULL_VOID(data);
65 data_ = data->GetRSData();
66 #endif
67 }
68
69 #ifndef USE_ROSEN_DRAWING
MakeSkiaImage()70 RefPtr<CanvasImage> ImageDecoder::MakeSkiaImage()
71 #else
72 RefPtr<CanvasImage> ImageDecoder::MakeDrawingImage()
73 #endif
74 {
75 CHECK_NULL_RETURN(obj_ && data_, nullptr);
76 ACE_SCOPED_TRACE("MakeSkiaImage %s", obj_->GetSourceInfo().ToString().c_str());
77 // check compressed image cache
78 {
79 auto image = QueryCompressedCache();
80 if (image) {
81 LOGD("QueryCompressedCache hit: %{public}s", obj_->GetSourceInfo().ToString().c_str());
82 return image;
83 }
84 }
85
86 #ifndef USE_ROSEN_DRAWING
87 auto image = ResizeSkImage();
88 CHECK_NULL_RETURN(image, nullptr);
89 #else
90 auto skImage = ResizeSkImage();
91 CHECK_NULL_RETURN(skImage, nullptr);
92 auto image = std::make_shared<RSImage>(&skImage);
93 #endif
94 auto canvasImage = CanvasImage::Create(&image);
95
96 if (ImageCompressor::GetInstance()->CanCompress()) {
97 #ifndef USE_ROSEN_DRAWING
98 TryCompress(DynamicCast<SkiaImage>(canvasImage));
99 #else
100 TryCompress(DynamicCast<DrawingImage>(canvasImage));
101 #endif
102 }
103 return canvasImage;
104 }
105
MakePixmapImage()106 RefPtr<CanvasImage> ImageDecoder::MakePixmapImage()
107 {
108 CHECK_NULL_RETURN(obj_ && data_, nullptr);
109 ACE_SCOPED_TRACE("MakePixmapImage %s", obj_->GetSourceInfo().ToString().c_str());
110
111 #ifndef USE_ROSEN_DRAWING
112 auto source = ImageSource::Create(data_->bytes(), data_->size());
113 #else
114 auto source = ImageSource::Create(static_cast<const uint8_t*>(data_->GetData()), data_->GetSize());
115 #endif
116 CHECK_NULL_RETURN(source, nullptr);
117
118 auto width = std::lround(desiredSize_.Width());
119 auto height = std::lround(desiredSize_.Height());
120 auto pixmap = source->CreatePixelMap({ width, height });
121 CHECK_NULL_RETURN(pixmap, nullptr);
122 auto image = PixelMapImage::Create(pixmap);
123 LOGD("decode to pixmap, desiredSize = %{public}s, pixmap size = %{public}d x %{public}d",
124 desiredSize_.ToString().c_str(), image->GetWidth(), image->GetHeight());
125
126 return image;
127 }
128
ForceResizeImage(const sk_sp<SkImage> & image,const SkImageInfo & info)129 sk_sp<SkImage> ImageDecoder::ForceResizeImage(const sk_sp<SkImage>& image, const SkImageInfo& info)
130 {
131 ACE_FUNCTION_TRACE();
132 SkBitmap bitmap;
133 bitmap.allocPixels(info);
134
135 #ifdef NEW_SKIA
136 auto res = image->scalePixels(
137 bitmap.pixmap(), SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone), SkImage::kDisallow_CachingHint);
138 #else
139 auto res = image->scalePixels(bitmap.pixmap(), kLow_SkFilterQuality, SkImage::kDisallow_CachingHint);
140 #endif
141 CHECK_NULL_RETURN(res, image);
142
143 bitmap.setImmutable();
144 return SkImage::MakeFromBitmap(bitmap);
145 }
146
ResizeSkImage()147 sk_sp<SkImage> ImageDecoder::ResizeSkImage()
148 {
149 #ifndef USE_ROSEN_DRAWING
150 auto encodedImage = SkImage::MakeFromEncoded(data_);
151 #else
152 CHECK_NULL_RETURN(data_, nullptr);
153 auto skData = data_->GetImpl<Rosen::Drawing::SkiaData>()->GetSkData();
154 auto encodedImage = SkImage::MakeFromEncoded(skData);
155 #endif
156 CHECK_NULL_RETURN_NOLOG(desiredSize_.IsPositive(), encodedImage);
157
158 auto width = std::lround(desiredSize_.Width());
159 auto height = std::lround(desiredSize_.Height());
160
161 #ifndef USE_ROSEN_DRAWING
162 auto codec = SkCodec::MakeFromData(data_);
163 #else
164 auto codec = SkCodec::MakeFromData(skData);
165 #endif
166 CHECK_NULL_RETURN(codec, {});
167 auto info = codec->getInfo();
168
169 // sourceSize is set by developer, then we will force scaling to [TargetSize] using SkImage::scalePixels,
170 // this method would succeed even if the codec doesn't support that size.
171 if (forceResize_) {
172 info = info.makeWH(width, height);
173 return ForceResizeImage(encodedImage, info);
174 }
175
176 if ((info.width() > width && info.height() > height)) {
177 // If the image is larger than the target size, we will scale it down to the target size.
178 // DesiredSize might not be compatible with the codec, so we find the closest size supported by the codec
179 auto scale = std::max(static_cast<float>(width) / info.width(), static_cast<float>(height) / info.height());
180 auto idealSize = codec->getScaledDimensions(scale);
181 LOGD("desiredSize = %{public}s, codec idealSize: %{public}dx%{public}d", desiredSize_.ToString().c_str(),
182 idealSize.width(), idealSize.height());
183
184 info = info.makeWH(idealSize.width(), idealSize.height());
185 SkBitmap bitmap;
186 bitmap.allocPixels(info);
187 auto res = codec->getPixels(info, bitmap.getPixels(), bitmap.rowBytes());
188 CHECK_NULL_RETURN(res == SkCodec::kSuccess, encodedImage);
189 return SkImage::MakeFromBitmap(bitmap);
190 }
191 return encodedImage;
192 }
193
QueryCompressedCache()194 RefPtr<CanvasImage> ImageDecoder::QueryCompressedCache()
195 {
196 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
197 auto cachedData = ImageLoader::LoadImageDataFromFileCache(key, ".astc");
198 CHECK_NULL_RETURN_NOLOG(cachedData, {});
199
200 #ifndef USE_ROSEN_DRAWING
201 auto skiaImageData = AceType::DynamicCast<SkiaImageData>(cachedData);
202 CHECK_NULL_RETURN(skiaImageData, {});
203 auto stripped = ImageCompressor::StripFileHeader(skiaImageData->GetSkData());
204 LOGI("use astc cache %{public}s", key.c_str());
205
206 // create encoded SkImage to use its uniqueId
207 auto image = SkImage::MakeFromEncoded(data_);
208 auto canvasImage = AceType::DynamicCast<SkiaImage>(CanvasImage::Create(&image));
209 #else
210 auto rosenImageData = AceType::DynamicCast<DrawingImageData>(cachedData);
211 CHECK_NULL_RETURN(rosenImageData, {});
212 auto stripped = ImageCompressor::StripFileHeader(rosenImageData->GetRSData());
213 LOGI("use astc cache %{public}s", key.c_str());
214
215 // create encoded SkImage to use its uniqueId
216 CHECK_NULL_RETURN(data_, {});
217 auto skData = data_->GetImpl<Rosen::Drawing::SkiaData>()->GetSkData();
218 auto skImage = SkImage::MakeFromEncoded(skData);
219 std::shared_ptr<RSImage> image = nullptr;
220 if (skImage) {
221 image = std::make_shared<RSImage>(&skImage);
222 }
223 auto canvasImage = AceType::DynamicCast<DrawingImage>(CanvasImage::Create(&image));
224 #endif
225 // round width and height to nearest int
226 int32_t dstWidth = std::lround(desiredSize_.Width());
227 int32_t dstHeight = std::lround(desiredSize_.Height());
228 canvasImage->SetCompressData(stripped, dstWidth, dstHeight);
229 #ifndef USE_ROSEN_DRAWING
230 canvasImage->ReplaceSkImage(nullptr);
231 #else
232 canvasImage->ReplaceRSImage(nullptr);
233 #endif
234 return canvasImage;
235 }
236
237 #ifndef USE_ROSEN_DRAWING
TryCompress(const RefPtr<SkiaImage> & image)238 void ImageDecoder::TryCompress(const RefPtr<SkiaImage>& image)
239 #else
240 void ImageDecoder::TryCompress(const RefPtr<DrawingImage>& image)
241 #endif
242 {
243 #ifdef UPLOAD_GPU_DISABLED
244 // If want to dump draw command or gpu disabled, should use CPU image.
245 return;
246 #else
247 // decode image to texture if not decoded
248 #ifndef USE_ROSEN_DRAWING
249 auto skImage = image->GetImage();
250 CHECK_NULL_VOID(skImage);
251 auto rasterizedImage = skImage->makeRasterImage();
252 CHECK_NULL_VOID(rasterizedImage);
253 ACE_DCHECK(!rasterizedImage->isTextureBacked());
254 SkPixmap pixmap;
255 CHECK_NULL_VOID(rasterizedImage->peekPixels(&pixmap));
256 auto width = pixmap.width();
257 auto height = pixmap.height();
258 // try compress image
259 if (ImageCompressor::GetInstance()->CanCompress()) {
260 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
261 auto compressData = ImageCompressor::GetInstance()->GpuCompress(key, pixmap, width, height);
262 ImageCompressor::GetInstance()->WriteToFile(key, compressData, { width, height });
263 if (compressData) {
264 // replace skImage of [CanvasImage] with [rasterizedImage]
265 image->SetCompressData(compressData, width, height);
266 image->ReplaceSkImage(nullptr);
267 } else {
268 image->ReplaceSkImage(rasterizedImage);
269 }
270 #else
271 auto rsImage = image->GetImage();
272 CHECK_NULL_VOID(rsImage);
273 RSBitmapFormat rsBitmapFormat { rsImage->GetColorType(), rsImage->GetAlphaType() };
274 RSBitmap rsBitmap;
275 rsBitmap.Build(rsImage->GetWidth(), rsImage->GetHeight(), rsBitmapFormat);
276 CHECK_NULL_VOID(rsImage->ReadPixels(rsBitmap, 0, 0));
277 auto width = rsBitmap.GetWidth();
278 auto height = rsBitmap.GetHeight();
279 // try compress image
280 if (ImageCompressor::GetInstance()->CanCompress()) {
281 auto key = ImageUtils::GenerateImageKey(obj_->GetSourceInfo(), desiredSize_);
282 auto compressData = ImageCompressor::GetInstance()->GpuCompress(key, rsBitmap, width, height);
283 ImageCompressor::GetInstance()->WriteToFile(key, compressData, { width, height });
284 if (compressData) {
285 // replace rsImage of [CanvasImage] with [rasterizedImage]
286 image->SetCompressData(compressData, width, height);
287 image->ReplaceRSImage(nullptr);
288 } else {
289 auto rasterizedImage = std::make_shared<RSImage>();
290 rasterizedImage->BuildFromBitmap(rsBitmap);
291 image->ReplaceRSImage(rasterizedImage);
292 }
293 #endif
294 auto taskExecutor = Container::CurrentTaskExecutor();
295 auto releaseTask = ImageCompressor::GetInstance()->ScheduleReleaseTask();
296 if (taskExecutor) {
297 taskExecutor->PostDelayedTask(releaseTask, TaskExecutor::TaskType::UI, ImageCompressor::releaseTimeMs);
298 } else {
299 ImageUtils::PostToBg(std::move(releaseTask));
300 }
301 }
302 SkGraphics::PurgeResourceCache();
303 #endif
304 }
305 } // namespace OHOS::Ace::NG
306