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