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