1 /*
2  * Copyright (c) 2021-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 "core/image/image_object.h"
17 
18 #ifdef USE_ROSEN_DRAWING
19 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
20 #endif
21 
22 #include "base/thread/background_task_executor.h"
23 #include "core/common/container.h"
24 #include "core/components/image/render_image.h"
25 #include "core/image/sk_image_cache.h"
26 #include "core/image/image_compressor.h"
27 
28 #ifdef APNG_IMAGE_SUPPORT
29 #include "core/image/apng/apng_image_decoder.h"
30 #include "core/image/apng/apng_image_object.h"
31 #endif
32 
33 namespace OHOS::Ace {
34 
GenerateCacheKey(const ImageSourceInfo & srcInfo,Size targetImageSize)35 std::string ImageObject::GenerateCacheKey(const ImageSourceInfo& srcInfo, Size targetImageSize)
36 {
37     return srcInfo.GetKey() + std::to_string(static_cast<int32_t>(targetImageSize.Width())) +
38            std::to_string(static_cast<int32_t>(targetImageSize.Height()));
39 }
40 
41 #ifndef USE_ROSEN_DRAWING
BuildImageObject(ImageSourceInfo source,const RefPtr<PipelineBase> context,const sk_sp<SkData> & skData,bool useSkiaSvg)42 RefPtr<ImageObject> ImageObject::BuildImageObject(
43     ImageSourceInfo source, const RefPtr<PipelineBase> context, const sk_sp<SkData>& skData, bool useSkiaSvg)
44 #else
45 RefPtr<ImageObject> ImageObject::BuildImageObject(
46     ImageSourceInfo source, const RefPtr<PipelineBase> context, const std::shared_ptr<RSData>& rsData, bool useSkiaSvg)
47 #endif
48 {
49     // build svg image object.
50     if (source.IsSvg()) {
51 #ifdef NG_BUILD
52         return nullptr;
53 #else
54 #ifndef USE_ROSEN_DRAWING
55         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
56 #else
57         if (rsData == nullptr) {
58             return nullptr;
59         }
60         auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
61         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
62 #endif
63         if (!svgStream) {
64             return nullptr;
65         }
66         auto color = source.GetFillColor();
67         if (!useSkiaSvg) {
68             return Ace::GetImageSvgDomObj(source, svgStream, context, color);
69         } else {
70             uint64_t colorValue = 0;
71             if (color.has_value()) {
72                 colorValue = color.value().GetValue();
73                 // skia svg relies on the 32th bit to determine whether or not to use the color we set.
74                 colorValue = colorValue | (static_cast<int64_t>(0b1) << 32);
75             }
76             auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, colorValue);
77             return skiaDom ? MakeRefPtr<SvgSkiaImageObject>(source, Size(), 1, skiaDom) : nullptr;
78         }
79 #endif
80     }
81 
82     // if is png or apng check
83 #ifdef APNG_IMAGE_SUPPORT
84     if (source.isPng()) {
85 #ifndef USE_ROSEN_DRAWING
86         auto apngDecoder = AceType::MakeRefPtr<PNGImageDecoder>(skData);
87 #else
88         auto apngDecoder = AceType::MakeRefPtr<PNGImageDecoder>(data);
89 #endif
90         if (apngDecoder && apngDecoder->isApng()) {
91             if (!apngDecoder->DecodeImage()) {
92                 return nullptr;
93             }
94 
95             Size imageSize = apngDecoder->GetImageSize();
96             uint32_t frameCount = apngDecoder->GetFrameCount();
97 #ifndef USE_ROSEN_DRAWING
98             return MakeRefPtr<ApngImageObject>(source, imageSize, frameCount, skData, apngDecoder);
99 #else
100             return MakeRefPtr<ApngImageObject>(source, imageSize, frameCount, data, apngDecoder);
101 #endif
102         }
103     }
104 #endif
105 
106     // build normal pixel image object.
107 #ifndef USE_ROSEN_DRAWING
108     auto codec = SkCodec::MakeFromData(skData);
109 #else
110     if (rsData == nullptr) {
111         return nullptr;
112     }
113     auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
114     auto codec = SkCodec::MakeFromData(skData);
115 #endif
116     int32_t totalFrames = 1;
117     Size imageSize;
118     if (codec) {
119         totalFrames = codec->getFrameCount();
120         switch (codec->getOrigin()) {
121             case SkEncodedOrigin::kLeftTop_SkEncodedOrigin:
122             case SkEncodedOrigin::kRightTop_SkEncodedOrigin:
123             case SkEncodedOrigin::kRightBottom_SkEncodedOrigin:
124             case SkEncodedOrigin::kLeftBottom_SkEncodedOrigin:
125                 imageSize.SetSize(Size(codec->dimensions().fHeight, codec->dimensions().fWidth));
126                 break;
127             default:
128                 imageSize.SetSize(Size(codec->dimensions().fWidth, codec->dimensions().fHeight));
129         }
130     }
131 #ifndef USE_ROSEN_DRAWING
132     if (totalFrames == 1) {
133         return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, skData);
134     } else {
135         return CreateAnimatedImageObject(source, imageSize, totalFrames, skData);
136     }
137 #else
138     if (totalFrames == 1) {
139         return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, rsData);
140     } else {
141         return CreateAnimatedImageObject(source, imageSize, totalFrames, rsData);
142     }
143 #endif
144 }
145 
MeasureForImage(RefPtr<RenderImage> image)146 Size ImageObject::MeasureForImage(RefPtr<RenderImage> image)
147 {
148     return image->MeasureForNormalImage();
149 }
150 
PerformLayoutImageObject(RefPtr<RenderImage> image)151 void SvgImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
152 {
153     image->PerformLayoutSvgImage();
154 }
155 
MeasureForImage(RefPtr<RenderImage> image)156 Size SvgImageObject::MeasureForImage(RefPtr<RenderImage> image)
157 {
158     return image->MeasureForSvgImage();
159 }
160 
PerformLayoutImageObject(RefPtr<RenderImage> image)161 void SvgSkiaImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image) {}
162 
MeasureForImage(RefPtr<RenderImage> image)163 Size SvgSkiaImageObject::MeasureForImage(RefPtr<RenderImage> image)
164 {
165     return image->MeasureForSvgImage();
166 }
167 
UploadToGpuForRender(const WeakPtr<PipelineBase> & context,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback,const Size & imageSize,bool forceResize,bool syncMode)168 void StaticImageObject::UploadToGpuForRender(const WeakPtr<PipelineBase>& context,
169     const UploadSuccessCallback& successCallback,
170     const FailedCallback& failedCallback, const Size& imageSize, bool forceResize, bool syncMode)
171 {
172 #ifndef USE_ROSEN_DRAWING
173     auto task = [context, successCallback, failedCallback, imageSize, forceResize, skData = skData_,
174 #else
175     auto task = [context, successCallback, failedCallback, imageSize, forceResize, rsData = data_,
176 #endif
177                     imageSource = imageSource_, id = Container::CurrentId()]() mutable {
178         ContainerScope scope(id);
179         auto pipelineContext = context.Upgrade();
180         if (!pipelineContext) {
181             LOGE("pipeline context has been released.");
182             return;
183         }
184         auto taskExecutor = pipelineContext->GetTaskExecutor();
185         if (!taskExecutor) {
186             LOGE("task executor is null.");
187             return;
188         }
189 
190         auto key = GenerateCacheKey(imageSource, imageSize);
191         // is already uploaded
192         if (!ImageProvider::TryUploadingImage(key, successCallback, failedCallback)) {
193             LOGI("other thread is uploading same image to gpu : %{private}s", imageSource.ToString().c_str());
194             return;
195         }
196 
197         RefPtr<NG::CanvasImage> cachedFlutterImage;
198         auto imageCache = pipelineContext->GetImageCache();
199         if (imageCache) {
200             auto cachedImage = imageCache->GetCacheImage(key);
201             if (cachedImage) {
202 #ifndef USE_ROSEN_DRAWING
203                 auto skImage = cachedImage->imagePtr;
204                 cachedFlutterImage = NG::CanvasImage::Create(&skImage);
205 #else
206                 auto rsImage = cachedImage->imagePtr;
207                 cachedFlutterImage = NG::CanvasImage::Create(&rsImage);
208 #endif
209             }
210         }
211 
212         // found cached image obj (can be rendered)
213         if (cachedFlutterImage) {
214             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, cachedFlutterImage);
215             return;
216         }
217 
218         auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, key,
219                             id = Container::CurrentId()](
220 #ifndef USE_ROSEN_DRAWING
221                             sk_sp<SkImage> image, sk_sp<SkData> compressData) {
222 #else
223                             std::shared_ptr<RSImage> image, std::shared_ptr<RSData> compressData) {
224 #endif
225 
226             if (!image && !compressData.get()) {
227                 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
228                     "Image data may be broken or absent in upload callback.");
229             }
230             ContainerScope scope(id);
231 #ifndef USE_ROSEN_DRAWING
232             sk_sp<SkImage> skImage = image;
233             auto canvasImage = NG::CanvasImage::Create(&skImage);
234 #else
235             std::shared_ptr<RSImage> rsImage = image;
236             auto canvasImage = NG::CanvasImage::Create(&rsImage);
237 #endif
238             int32_t width = static_cast<int32_t>(imageSize.Width() + 0.5);
239             int32_t height = static_cast<int32_t>(imageSize.Height() + 0.5);
240             canvasImage->SetRawCompressData(&compressData, width, height);
241 
242             if (imageCache) {
243 #ifndef USE_ROSEN_DRAWING
244                 imageCache->CacheImage(key, std::make_shared<CachedImage>(skImage));
245 #else
246                 imageCache->CacheImage(key, std::make_shared<CachedImage>(rsImage));
247 #endif
248             }
249             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, canvasImage);
250         };
251         // here skdata is origin pic, also have 'target size'
252         // if have skdata, means origin pic is rendered first time
253         // if no skdata, means origin pic has shown, and has been cleared
254         // we try to use small image or compressed image instead of origin pic.
255 #ifndef USE_ROSEN_DRAWING
256         sk_sp<SkData> stripped;
257 #else
258         std::shared_ptr<RSData> stripped;
259 #endif
260         if (ImageCompressor::GetInstance()->CanCompress()) {
261             // load compressed
262             auto compressedData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key, ".astc");
263             stripped = ImageCompressor::StripFileHeader(compressedData);
264         }
265         auto smallData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key);
266         if (smallData) {
267 #ifndef USE_ROSEN_DRAWING
268             skData = smallData;
269 #else
270             rsData = smallData;
271 #endif
272         }
273 
274 #ifndef USE_ROSEN_DRAWING
275         if (!skData) {
276             skData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
277             if (!skData) {
278 #else
279         if (!rsData) {
280             rsData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
281             if (!rsData) {
282 #endif
283                 LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str());
284                 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
285                     "Image data may be broken or absent, please check if image file or image data is valid.");
286                 return;
287             }
288         }
289 
290         // make lazy image from file
291 #ifndef USE_ROSEN_DRAWING
292         auto rawImage = SkImage::MakeFromEncoded(skData);
293         if (!rawImage) {
294 #else
295         auto rawImage = std::make_shared<RSImage>();
296         bool result = rawImage->MakeFromEncoded(rsData);
297         if (!result) {
298 #endif
299             LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str());
300             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
301                 "Image data may be broken, please check if image file or image data is broken.");
302             return;
303         }
304 #ifndef USE_ROSEN_DRAWING
305         sk_sp<SkImage> image;
306         if (smallData) {
307             image = rawImage;
308         } else {
309             image = ImageProvider::ResizeSkImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
310         }
311         ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
312         skData = nullptr;
313 #else
314         std::shared_ptr<RSImage> image;
315         if (smallData) {
316             image = rawImage;
317         } else {
318             image = ImageProvider::ResizeDrawingImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
319         }
320         ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
321         rsData = nullptr;
322 #endif
323     };
324     if (syncMode) {
325         task();
326         return;
327     }
328     uploadForPaintTask_ = CancelableTask(std::move(task));
329     BackgroundTaskExecutor::GetInstance().PostTask(uploadForPaintTask_);
330 }
331 
332 bool StaticImageObject::CancelBackgroundTasks()
333 {
334     return uploadForPaintTask_ ? uploadForPaintTask_.Cancel(false) : false;
335 }
336 
337 void PixelMapImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
338 {
339     image->PerformLayoutPixmap();
340 }
341 
342 Size PixelMapImageObject::MeasureForImage(RefPtr<RenderImage> image)
343 {
344     return image->MeasureForPixmap();
345 }
346 
347 } // namespace OHOS::Ace
348