• 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 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
19 
20 #include "base/thread/background_task_executor.h"
21 #include "core/common/container.h"
22 #include "core/components/image/render_image.h"
23 #include "core/image/image_cache.h"
24 #include "core/image/image_compressor.h"
25 
26 namespace OHOS::Ace {
27 
GenerateCacheKey(const ImageSourceInfo & srcInfo,Size targetImageSize)28 std::string ImageObject::GenerateCacheKey(const ImageSourceInfo& srcInfo, Size targetImageSize)
29 {
30     return srcInfo.GetKey() + std::to_string(static_cast<int32_t>(targetImageSize.Width())) +
31            std::to_string(static_cast<int32_t>(targetImageSize.Height()));
32 }
33 
BuildImageObject(ImageSourceInfo source,const RefPtr<PipelineBase> context,const std::shared_ptr<RSData> & rsData,bool useSkiaSvg)34 RefPtr<ImageObject> ImageObject::BuildImageObject(
35     ImageSourceInfo source, const RefPtr<PipelineBase> context, const std::shared_ptr<RSData>& rsData, bool useSkiaSvg)
36 {
37     // build svg image object.
38     if (source.IsSvg()) {
39 #ifdef NG_BUILD
40         return nullptr;
41 #else
42         if (rsData == nullptr) {
43             return nullptr;
44         }
45         auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
46         const auto svgStream = std::make_unique<SkMemoryStream>(skData);
47         if (!svgStream) {
48             return nullptr;
49         }
50         auto color = source.GetFillColor();
51         if (!useSkiaSvg) {
52             return Ace::GetImageSvgDomObj(source, svgStream, context, color);
53         } else {
54             uint64_t colorValue = 0;
55             if (color.has_value()) {
56                 colorValue = color.value().GetValue();
57                 // skia svg relies on the 32th bit to determine whether or not to use the color we set.
58                 colorValue = colorValue | (static_cast<int64_t>(0b1) << 32);
59             }
60             auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, colorValue);
61             return skiaDom ? MakeRefPtr<SvgSkiaImageObject>(source, Size(), 1, skiaDom) : nullptr;
62         }
63 #endif
64     }
65 
66     // build normal pixel image object.
67     if (rsData == nullptr) {
68         return nullptr;
69     }
70     auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
71     auto codec = SkCodec::MakeFromData(skData);
72     int32_t totalFrames = 1;
73     Size imageSize;
74     if (codec) {
75         totalFrames = codec->getFrameCount();
76         switch (codec->getOrigin()) {
77             case SkEncodedOrigin::kLeftTop_SkEncodedOrigin:
78             case SkEncodedOrigin::kRightTop_SkEncodedOrigin:
79             case SkEncodedOrigin::kRightBottom_SkEncodedOrigin:
80             case SkEncodedOrigin::kLeftBottom_SkEncodedOrigin:
81                 imageSize.SetSize(Size(codec->dimensions().fHeight, codec->dimensions().fWidth));
82                 break;
83             default:
84                 imageSize.SetSize(Size(codec->dimensions().fWidth, codec->dimensions().fHeight));
85         }
86     }
87     if (totalFrames == 1) {
88         return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, rsData);
89     } else {
90         return CreateAnimatedImageObject(source, imageSize, totalFrames, rsData);
91     }
92 }
93 
MeasureForImage(RefPtr<RenderImage> image)94 Size ImageObject::MeasureForImage(RefPtr<RenderImage> image)
95 {
96     return image->MeasureForNormalImage();
97 }
98 
PerformLayoutImageObject(RefPtr<RenderImage> image)99 void SvgImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
100 {
101     image->PerformLayoutSvgImage();
102 }
103 
MeasureForImage(RefPtr<RenderImage> image)104 Size SvgImageObject::MeasureForImage(RefPtr<RenderImage> image)
105 {
106     return image->MeasureForSvgImage();
107 }
108 
PerformLayoutImageObject(RefPtr<RenderImage> image)109 void SvgSkiaImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image) {}
110 
MeasureForImage(RefPtr<RenderImage> image)111 Size SvgSkiaImageObject::MeasureForImage(RefPtr<RenderImage> image)
112 {
113     return image->MeasureForSvgImage();
114 }
115 
UploadToGpuForRender(const WeakPtr<PipelineBase> & context,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback,const Size & imageSize,bool forceResize,bool syncMode)116 void StaticImageObject::UploadToGpuForRender(const WeakPtr<PipelineBase>& context,
117     const UploadSuccessCallback& successCallback,
118     const FailedCallback& failedCallback, const Size& imageSize, bool forceResize, bool syncMode)
119 {
120     auto task = [context, successCallback, failedCallback, imageSize, forceResize, rsData = data_,
121                     imageSource = imageSource_, id = Container::CurrentId()]() mutable {
122         ContainerScope scope(id);
123         auto pipelineContext = context.Upgrade();
124         if (!pipelineContext) {
125             LOGE("pipeline context has been released.");
126             return;
127         }
128         auto taskExecutor = pipelineContext->GetTaskExecutor();
129         if (!taskExecutor) {
130             LOGE("task executor is null.");
131             return;
132         }
133 
134         auto key = GenerateCacheKey(imageSource, imageSize);
135         // is already uploaded
136         if (!ImageProvider::TryUploadingImage(key, successCallback, failedCallback)) {
137             LOGI("other thread is uploading same image to gpu : %{private}s", imageSource.ToString().c_str());
138             return;
139         }
140 
141         RefPtr<NG::CanvasImage> cachedFlutterImage;
142         auto imageCache = pipelineContext->GetImageCache();
143         if (imageCache) {
144             auto cachedImage = imageCache->GetCacheImage(key);
145             if (cachedImage) {
146                 auto rsImage = cachedImage->imagePtr;
147                 cachedFlutterImage = NG::CanvasImage::Create(&rsImage);
148             }
149         }
150 
151         // found cached image obj (can be rendered)
152         if (cachedFlutterImage) {
153             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, cachedFlutterImage);
154             return;
155         }
156 
157         auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, key,
158                             id = Container::CurrentId()](
159                             std::shared_ptr<RSImage> image, std::shared_ptr<RSData> compressData) {
160 
161             if (!image && !compressData.get()) {
162                 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
163                     "Image data may be broken or absent in upload callback.");
164             }
165             ContainerScope scope(id);
166             std::shared_ptr<RSImage> rsImage = image;
167             auto canvasImage = NG::CanvasImage::Create(&rsImage);
168             int32_t width = static_cast<int32_t>(imageSize.Width() + 0.5);
169             int32_t height = static_cast<int32_t>(imageSize.Height() + 0.5);
170             canvasImage->SetRawCompressData(&compressData, width, height);
171 
172             if (imageCache) {
173                 imageCache->CacheImage(key, std::make_shared<CachedImage>(rsImage));
174             }
175             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, canvasImage);
176         };
177         // here skdata is origin pic, also have 'target size'
178         // if have skdata, means origin pic is rendered first time
179         // if no skdata, means origin pic has shown, and has been cleared
180         // we try to use small image or compressed image instead of origin pic.
181         std::shared_ptr<RSData> stripped;
182         if (ImageCompressor::GetInstance()->CanCompress()) {
183             // load compressed
184             auto compressedData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key, ".astc");
185             stripped = ImageCompressor::StripFileHeader(compressedData);
186         }
187         auto smallData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key);
188         if (smallData) {
189             rsData = smallData;
190         }
191 
192         if (!rsData) {
193             rsData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
194             if (!rsData) {
195                 LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str());
196                 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
197                     "Image data may be broken or absent, please check if image file or image data is valid.");
198                 return;
199             }
200         }
201 
202         // make lazy image from file
203         auto rawImage = std::make_shared<RSImage>();
204         bool result = rawImage->MakeFromEncoded(rsData);
205         if (!result) {
206             LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str());
207             ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
208                 "Image data may be broken, please check if image file or image data is broken.");
209             return;
210         }
211         std::shared_ptr<RSImage> image;
212         if (smallData) {
213             image = rawImage;
214         } else {
215             image = ImageProvider::ResizeDrawingImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
216         }
217         ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
218         rsData = nullptr;
219     };
220     if (syncMode) {
221         task();
222         return;
223     }
224     uploadForPaintTask_ = CancelableTask(std::move(task));
225     BackgroundTaskExecutor::GetInstance().PostTask(uploadForPaintTask_);
226 }
227 
CancelBackgroundTasks()228 bool StaticImageObject::CancelBackgroundTasks()
229 {
230     return uploadForPaintTask_ ? uploadForPaintTask_.Cancel(false) : false;
231 }
232 
PerformLayoutImageObject(RefPtr<RenderImage> image)233 void PixelMapImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
234 {
235     image->PerformLayoutPixmap();
236 }
237 
MeasureForImage(RefPtr<RenderImage> image)238 Size PixelMapImageObject::MeasureForImage(RefPtr<RenderImage> image)
239 {
240     return image->MeasureForPixmap();
241 }
242 
243 } // namespace OHOS::Ace
244