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