• 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 "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